sqlsoup-0.9.1/0000775000175000017500000000000012654224057014324 5ustar classicclassic00000000000000sqlsoup-0.9.1/setup.py0000644000175000017500000000230012241256114016016 0ustar classicclassic00000000000000from setuptools import setup import os import re v = open(os.path.join(os.path.dirname(__file__), 'sqlsoup.py')) VERSION = re.compile(r".*__version__ = '(.*?)'", re.S).match(v.read()).group(1) v.close() readme = os.path.join(os.path.dirname(__file__), 'README.rst') setup(name='sqlsoup', version=VERSION, description="A one step database access tool, built on the SQLAlchemy ORM.", long_description=open(readme).read(), classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Database :: Front-Ends', ], keywords='SQLAlchemy ORM', author='Mike Bayer', author_email='mike@zzzcomputing.com', url='http://bitbucket.org/zzzeek/sqlsoup', license='MIT', py_modules=('sqlsoup',), tests_require = ['nose >= 0.11'], test_suite = "nose.collector", zip_safe=False, install_requires=[ 'SQLAlchemy>=0.7.0', ], ) sqlsoup-0.9.1/sqlsoup.egg-info/0000775000175000017500000000000012654224057017524 5ustar classicclassic00000000000000sqlsoup-0.9.1/sqlsoup.egg-info/SOURCES.txt0000644000175000017500000000070312654224057021406 0ustar classicclassic00000000000000LICENSE MANIFEST.in README.rst setup.py sqlsoup.py docs/build/Makefile docs/build/api.rst docs/build/changelog.rst docs/build/conf.py docs/build/front.rst docs/build/index.rst docs/build/requirements.txt docs/build/tutorial.rst sqlsoup.egg-info/PKG-INFO sqlsoup.egg-info/SOURCES.txt sqlsoup.egg-info/dependency_links.txt sqlsoup.egg-info/not-zip-safe sqlsoup.egg-info/requires.txt sqlsoup.egg-info/top_level.txt tests/__init__.py tests/test_sqlsoup.pysqlsoup-0.9.1/sqlsoup.egg-info/PKG-INFO0000644000175000017500000000331112654224057020615 0ustar classicclassic00000000000000Metadata-Version: 1.1 Name: sqlsoup Version: 0.9.1 Summary: A one step database access tool, built on the SQLAlchemy ORM. Home-page: http://bitbucket.org/zzzeek/sqlsoup Author: Mike Bayer Author-email: mike@zzzcomputing.com License: MIT Description: ======= SQLSoup ======= SQLSoup provides a convenient way to map Python objects to relational database tables, with no declarative code of any kind. It's built on top of the `SQLAlchemy `_ ORM and provides a super-minimalistic interface to an existing database. Usage is as simple as:: import sqlsoup db = sqlsoup.SQLSoup("postgresql://scott:tiger@localhost/test") for user in db.users.all(): print "user:", user.name db.users.filter_by(name="ed").update({"name":"jack"}) db.commit() Included for many years as an extension to SQLAlchemy itself, SQLSoup has been broken out into it's own project as of 2012. The community is encouraged to collaborate on Bitbucket with patches and features. Documentation and status of SQLSoup is at http://readthedocs.org/docs/sqlsoup/. Keywords: SQLAlchemy ORM Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Database :: Front-Ends sqlsoup-0.9.1/sqlsoup.egg-info/dependency_links.txt0000644000175000017500000000000112654224057023570 0ustar classicclassic00000000000000 sqlsoup-0.9.1/sqlsoup.egg-info/top_level.txt0000644000175000017500000000001012654224057022243 0ustar classicclassic00000000000000sqlsoup sqlsoup-0.9.1/sqlsoup.egg-info/not-zip-safe0000644000175000017500000000000112267766576021771 0ustar classicclassic00000000000000 sqlsoup-0.9.1/sqlsoup.egg-info/requires.txt0000644000175000017500000000002212654224057022114 0ustar classicclassic00000000000000SQLAlchemy>=0.7.0 sqlsoup-0.9.1/tests/0000775000175000017500000000000012654224057015466 5ustar classicclassic00000000000000sqlsoup-0.9.1/tests/test_sqlsoup.py0000644000175000017500000003626212517251766020621 0ustar classicclassic00000000000000import sqlsoup from tests import eq_, assert_raises, \ assert_raises_message from sqlalchemy import create_engine, or_, desc, select, func, exc, \ Table, util, Column, Integer from sqlalchemy.orm import scoped_session, sessionmaker import datetime import unittest class SQLSoupTest(unittest.TestCase): @classmethod def setup_class(cls): global engine engine = create_engine('sqlite://', echo=True) for sql in _ddl: engine.execute(sql) @classmethod def teardown_class(cls): engine.dispose() def setUp(self): for sql in _data: engine.execute(sql) def tearDown(self): sqlsoup.Session.remove() for sql in _teardown: engine.execute(sql) def test_map_to_attr_present(self): db = sqlsoup.SQLSoup(engine) users = db.users assert_raises_message( sqlsoup.SQLSoupError, "Attribute 'users' is already mapped", db.map_to, 'users', tablename='users' ) def test_map_to_table_not_string(self): db = sqlsoup.SQLSoup(engine) table = Table('users', db._metadata, Column('id', Integer, primary_key=True)) assert_raises_message( sqlsoup.ArgumentError, "'tablename' argument must be a string.", db.map_to, 'users', tablename=table ) def test_map_to_table_or_selectable(self): db = sqlsoup.SQLSoup(engine) table = Table('users', db._metadata, Column('id', Integer, primary_key=True)) assert_raises_message( sqlsoup.ArgumentError, "'tablename' and 'selectable' arguments are mutually exclusive", db.map_to, 'users', tablename='users', selectable=table ) def test_map_to_no_pk_selectable(self): db = sqlsoup.SQLSoup(engine) table = Table('users', db._metadata, Column('id', Integer)) assert_raises_message( sqlsoup.SQLSoupError, "table 'users' does not have a primary ", db.map_to, 'users', selectable=table ) def test_map_to_invalid_schema(self): db = sqlsoup.SQLSoup(engine) table = Table('users', db._metadata, Column('id', Integer)) assert_raises_message( sqlsoup.ArgumentError, "'tablename' argument is required when " "using 'schema'.", db.map_to, 'users', selectable=table, schema='hoho' ) def test_map_to_nothing(self): db = sqlsoup.SQLSoup(engine) assert_raises_message( sqlsoup.ArgumentError, "'tablename' or 'selectable' argument is " "required.", db.map_to, 'users', ) def test_map_to_string_not_selectable(self): db = sqlsoup.SQLSoup(engine) assert_raises_message( sqlsoup.ArgumentError, "'selectable' argument must be a " "table, select, join, or other " "selectable construct.", db.map_to, 'users', selectable='users' ) def test_bad_names(self): db = sqlsoup.SQLSoup(engine) str(db.bad_names.c.query) def test_load(self): db = sqlsoup.SQLSoup(engine) MappedUsers = db.users users = db.users.all() users.sort() eq_(users, [MappedUsers(name='Joe Student', email='student@example.ed', password='student', classname=None, admin=0), MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1)]) def test_order_by(self): db = sqlsoup.SQLSoup(engine) MappedUsers = db.users users = db.users.order_by(db.users.name).all() eq_(users, [MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1), MappedUsers(name='Joe Student', email='student@example.ed', password='student', classname=None, admin=0)]) def test_whereclause(self): db = sqlsoup.SQLSoup(engine) MappedUsers = db.users where = or_(db.users.name == 'Bhargan Basepair', db.users.email == 'student@example.ed') users = \ db.users.filter(where).order_by(desc(db.users.name)).all() eq_(users, [MappedUsers(name='Joe Student', email='student@example.ed', password='student', classname=None, admin=0), MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1)]) def test_first(self): db = sqlsoup.SQLSoup(engine) MappedUsers = db.users user = db.users.filter(db.users.name == 'Bhargan Basepair' ).one() eq_(user, MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1)) db.rollback() user = db.users.get('Bhargan Basepair') eq_(user, MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1)) db.rollback() user = db.users.filter_by(name='Bhargan Basepair').one() eq_(user, MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1)) db.rollback() def test_crud(self): # note we're testing autoflush here too... db = sqlsoup.SQLSoup(engine) MappedLoans = db.loans user = db.users.filter_by(name='Bhargan Basepair').one() book_id = db.books.filter_by(title='Regional Variation in Moss' ).first().id loan_insert = db.loans.insert(book_id=book_id, user_name=user.name) loan = db.loans.filter_by(book_id=2, user_name='Bhargan Basepair').one() eq_(loan, loan_insert) l2 = MappedLoans(book_id=2, user_name='Bhargan Basepair', loan_date=loan.loan_date) eq_(loan, l2) db.expunge(l2) db.delete(loan) loan = db.loans.filter_by(book_id=2, user_name='Bhargan Basepair').first() assert loan is None def test_cls_crud(self): db = sqlsoup.SQLSoup(engine) MappedUsers = db.users db.users.filter_by(name='Bhargan Basepair' ).update(dict(name='Some New Name')) u1 = db.users.filter_by(name='Some New Name').one() eq_(u1, MappedUsers(name='Some New Name', email='basepair@example.ed', password='basepair', classname=None, admin=1)) def test_map_table(self): db = sqlsoup.SQLSoup(engine) users = Table('users', db._metadata, autoload=True) MappedUsers = db.map(users) users = MappedUsers.order_by(db.users.name).all() eq_(users, [MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1), MappedUsers(name='Joe Student', email='student@example.ed', password='student', classname=None, admin=0)]) def test_mapped_join(self): db = sqlsoup.SQLSoup(engine) join1 = MappedJoin = db.join(db.users, db.loans, isouter=True) mj = join1.filter_by(name='Joe Student').all() eq_(mj, [MappedJoin( name='Joe Student', email='student@example.ed', password='student', classname=None, admin=0, book_id=1, user_name='Joe Student', loan_date=datetime.datetime(2006, 7, 12, 0, 0), )]) db.rollback() join2 = MappedJoin = db.join(join1, db.books) mj = join2.all() eq_(mj, [MappedJoin( name='Joe Student', email='student@example.ed', password='student', classname=None, admin=0, book_id=1, user_name='Joe Student', loan_date=datetime.datetime(2006, 7, 12, 0, 0), id=1, title='Mustards I Have Known', published_year='1989', authors='Jones', )]) eq_(db.with_labels(join1).c.keys(), [ 'users_name', 'users_email', 'users_password', 'users_classname', 'users_admin', 'loans_book_id', 'loans_user_name', 'loans_loan_date', ]) labeled_loans = db.with_labels(db.loans) eq_(db.join(db.users, labeled_loans, isouter=True).c.keys(), [ 'name', 'email', 'password', 'classname', 'admin', 'loans_book_id', 'loans_user_name', 'loans_loan_date', ]) def test_relations(self): db = sqlsoup.SQLSoup(engine) db.users.relate('loans', db.loans) MappedLoans = db.loans MappedUsers = db.users eq_(db.users.get('Joe Student').loans, [MappedLoans(book_id=1, user_name='Joe Student', loan_date=datetime.datetime(2006, 7, 12, 0, 0))]) db.rollback() eq_(db.users.filter(~db.users.loans.any()).all(), [MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1)]) db.rollback() del db._cache['users'] db.users.relate('loans', db.loans, order_by=db.loans.loan_date, cascade='all, delete-orphan') def test_relate_m2o(self): db = sqlsoup.SQLSoup(engine) db.loans.relate('user', db.users) u1 = db.users.filter(db.users.c.name=='Joe Student').one() eq_(db.loans.first().user, u1) def test_explicit_session(self): Session = scoped_session(sessionmaker()) db = sqlsoup.SQLSoup(engine, session=Session) try: MappedUsers = db.users sess = Session() assert db.users._query.session is db.users.session is sess row = db.users.insert(name='new name', email='new email') assert row in sess finally: sess.rollback() sess.close() def test_selectable(self): db = sqlsoup.SQLSoup(engine) MappedBooks = db.books b = db.books._table s = select([b.c.published_year, func.count('*').label('n')], from_obj=[b], group_by=[b.c.published_year]) s = s.alias('years_with_count') years_with_count = db.map(s, primary_key=[s.c.published_year]) eq_(years_with_count.filter_by(published_year='1989').all(), [MappedBooks(published_year='1989', n=1)]) def test_raw_sql(self): db = sqlsoup.SQLSoup(engine) rp = db.execute('select name, email from users order by name') eq_(rp.fetchall(), [('Bhargan Basepair', 'basepair@example.ed' ), ('Joe Student', 'student@example.ed')]) # test that execute() shares the same transactional context as # the session db.execute("update users set email='foo bar'") eq_(db.execute('select distinct email from users').fetchall(), [('foo bar', )]) db.rollback() eq_(db.execute('select distinct email from users').fetchall(), [('basepair@example.ed', ), ('student@example.ed', )]) def test_connection(self): db = sqlsoup.SQLSoup(engine) conn = db.connection() rp = conn.execute('select name, email from users order by name') eq_(rp.fetchall(), [('Bhargan Basepair', 'basepair@example.ed' ), ('Joe Student', 'student@example.ed')]) def test_entity(self): db = sqlsoup.SQLSoup(engine) tablename = 'loans' eq_(db.entity(tablename), db.loans) def test_entity_with_different_base(self): class subclass(object): pass db = sqlsoup.SQLSoup(engine, base=subclass) assert issubclass(db.entity('loans'), subclass) def test_filter_by_order_by(self): db = sqlsoup.SQLSoup(engine) MappedUsers = db.users users = \ db.users.filter_by(classname=None).order_by(db.users.name).all() eq_(users, [MappedUsers(name='Bhargan Basepair', email='basepair@example.ed', password='basepair', classname=None, admin=1), MappedUsers(name='Joe Student', email='student@example.ed', password='student', classname=None, admin=0)]) def test_no_pk_reflected(self): db = sqlsoup.SQLSoup(engine) assert_raises(sqlsoup.SQLSoupError, getattr, db, 'nopk') def test_nosuchtable(self): db = sqlsoup.SQLSoup(engine) assert_raises(exc.NoSuchTableError, getattr, db, 'nosuchtable') def test_dont_persist_alias(self): db = sqlsoup.SQLSoup(engine) MappedBooks = db.books b = db.books._table s = select([b.c.published_year, func.count('*').label('n')], from_obj=[b], group_by=[b.c.published_year]) s = s.alias('years_with_count') years_with_count = db.map(s, primary_key=[s.c.published_year]) assert_raises(sqlsoup.SQLSoupError, years_with_count.insert, published_year='2007', n=1) def test_clear(self): db = sqlsoup.SQLSoup(engine) eq_(db.loans.count(), 1) _ = db.loans.insert(book_id=1, user_name='Bhargan Basepair') db.expunge_all() db.flush() eq_(db.loans.count(), 1) _ddl = \ """ CREATE TABLE books ( id integer PRIMARY KEY, -- auto-increments in sqlite title text NOT NULL, published_year char(4) NOT NULL, authors text NOT NULL ); CREATE TABLE users ( name varchar(32) PRIMARY KEY, email varchar(128) NOT NULL, password varchar(128) NOT NULL, classname text, admin int NOT NULL -- 0 = false ); CREATE TABLE loans ( book_id int PRIMARY KEY REFERENCES books(id), user_name varchar(32) references users(name) ON DELETE SET NULL ON UPDATE CASCADE, loan_date datetime DEFAULT current_timestamp ); CREATE TABLE nopk ( i int ); CREATE TABLE bad_names ( id int primary key, query varchar(100) ) """.split(';') _data = \ """ insert into users(name, email, password, admin) values('Bhargan Basepair', 'basepair@example.ed', 'basepair', 1); insert into users(name, email, password, admin) values('Joe Student', 'student@example.ed', 'student', 0); insert into books(title, published_year, authors) values('Mustards I Have Known', '1989', 'Jones'); insert into books(title, published_year, authors) values('Regional Variation in Moss', '1971', 'Flim and Flam'); insert into loans(book_id, user_name, loan_date) values ( (select min(id) from books), (select name from users where name like 'Joe%'), '2006-07-12 0:0:0') ; """.split(';' ) _teardown = \ """ delete from loans; delete from books; delete from users; delete from nopk; """.split(';' ) sqlsoup-0.9.1/tests/__init__.py0000644000175000017500000000152112241256275017573 0ustar classicclassic00000000000000import re def eq_(a, b, msg=None): """Assert a == b, with repr messaging on failure.""" assert a == b, msg or "%r != %r" % (a, b) def ne_(a, b, msg=None): """Assert a != b, with repr messaging on failure.""" assert a != b, msg or "%r == %r" % (a, b) def assert_raises(except_cls, callable_, *args, **kw): try: callable_(*args, **kw) success = False except except_cls: success = True # assert outside the block so it works for AssertionError too ! assert success, "Callable did not raise an exception" def assert_raises_message(except_cls, msg, callable_, *args, **kwargs): try: callable_(*args, **kwargs) assert False, "Callable did not raise an exception" except except_cls as e: assert re.search(msg, str(e)), "%r !~ %s" % (msg, e) print(str(e)) sqlsoup-0.9.1/README.rst0000644000175000017500000000150512267766425016024 0ustar classicclassic00000000000000======= SQLSoup ======= SQLSoup provides a convenient way to map Python objects to relational database tables, with no declarative code of any kind. It's built on top of the `SQLAlchemy `_ ORM and provides a super-minimalistic interface to an existing database. Usage is as simple as:: import sqlsoup db = sqlsoup.SQLSoup("postgresql://scott:tiger@localhost/test") for user in db.users.all(): print "user:", user.name db.users.filter_by(name="ed").update({"name":"jack"}) db.commit() Included for many years as an extension to SQLAlchemy itself, SQLSoup has been broken out into it's own project as of 2012. The community is encouraged to collaborate on Bitbucket with patches and features. Documentation and status of SQLSoup is at http://readthedocs.org/docs/sqlsoup/. sqlsoup-0.9.1/sqlsoup.py0000644000175000017500000003700312654224013016375 0ustar classicclassic00000000000000""" """ from sqlalchemy import Table, MetaData, join from sqlalchemy import schema, sql, util from sqlalchemy.engine.base import Engine from sqlalchemy.orm import scoped_session, sessionmaker, mapper, \ class_mapper, relationship, session,\ object_session, attributes from sqlalchemy.orm.interfaces import MapperExtension, EXT_CONTINUE from sqlalchemy.sql import expression import sys py2k = sys.version_info < (3, 0) if py2k: string_types = basestring, else: string_types = string_types = str, __version__ = '0.9.1' __all__ = ['SQLSoupError', 'SQLSoup', 'SelectableClassType', 'TableClassType', 'Session'] Session = scoped_session(sessionmaker()) """SQLSoup's default session registry. This is an instance of :class:`sqlalchemy.orm.scoping.ScopedSession`, and provides a new :class:`sqlalchemy.orm.session.Session` object for each application thread which refers to it. """ class AutoAdd(MapperExtension): def __init__(self, scoped_session): self.scoped_session = scoped_session def instrument_class(self, mapper, class_): class_.__init__ = self._default__init__(mapper) def _default__init__(ext, mapper): def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) return __init__ def init_instance(self, mapper, class_, oldinit, instance, args, kwargs): session = self.scoped_session() state = attributes.instance_state(instance) session._save_impl(state) return EXT_CONTINUE def init_failed(self, mapper, class_, oldinit, instance, args, kwargs): sess = object_session(instance) if sess: sess.expunge(instance) return EXT_CONTINUE class SQLSoupError(Exception): pass class ArgumentError(SQLSoupError): pass # metaclass is necessary to expose class methods with getattr, e.g. # we want to pass db.users.select through to users._mapper.select class SelectableClassType(type): """Represent a SQLSoup mapping to a :class:`sqlalchemy.sql.expression.Selectable` construct, such as a table or SELECT statement. """ def insert(cls, **kwargs): raise SQLSoupError( 'SQLSoup can only modify mapped Tables (found: %s)' \ % cls._table.__class__.__name__ ) def __clause_element__(cls): return cls._table def __getattr__(cls, attr): if attr == '_query': # called during mapper init raise AttributeError() return getattr(cls._query, attr) class TableClassType(SelectableClassType): """Represent a SQLSoup mapping to a :class:`sqlalchemy.schema.Table` construct. This object is produced automatically when a table-name attribute is accessed from a :class:`.SQLSoup` instance. """ def insert(cls, **kwargs): o = cls() o.__dict__.update(kwargs) return o def relate(cls, propname, *args, **kwargs): """Produce a relationship between this mapped table and another one. This makes usage of SQLAlchemy's :func:`sqlalchemy.orm.relationship` construct. """ class_mapper(cls)._configure_property(propname, relationship(*args, **kwargs)) def _is_outer_join(selectable): if not isinstance(selectable, sql.Join): return False if selectable.isouter: return True return _is_outer_join(selectable.left) or _is_outer_join(selectable.right) def _selectable_name(selectable): if isinstance(selectable, sql.Alias): return _selectable_name(selectable.element) elif isinstance(selectable, sql.Select): return ''.join(_selectable_name(s) for s in selectable.froms) elif isinstance(selectable, schema.Table): return selectable.name.capitalize() else: x = selectable.__class__.__name__ if x[0] == '_': x = x[1:] return x def _class_for_table(session, engine, selectable, base_cls, mapper_kwargs): selectable = expression._clause_element_as_expr(selectable) mapname = 'Mapped' + _selectable_name(selectable) if py2k and isinstance(mapname, unicode): engine_encoding = engine.dialect.encoding mapname = mapname.encode(engine_encoding) if isinstance(selectable, Table): klass = TableClassType(mapname, (base_cls,), {}) else: klass = SelectableClassType(mapname, (base_cls,), {}) def _compare(self, o): L = list(self.__class__.c.keys()) L.sort() t1 = [getattr(self, k) for k in L] try: t2 = [getattr(o, k) for k in L] except AttributeError: raise TypeError('unable to compare with %s' % o.__class__) return t1, t2 # python2/python3 compatible system of # __cmp__ - __lt__ + __eq__ def __lt__(self, o): t1, t2 = _compare(self, o) return t1 < t2 def __eq__(self, o): t1, t2 = _compare(self, o) return t1 == t2 def __repr__(self): L = ["%s=%r" % (key, getattr(self, key, '')) for key in self.__class__.c.keys()] return '%s(%s)' % (self.__class__.__name__, ','.join(L)) for m in ['__eq__', '__repr__', '__lt__']: setattr(klass, m, eval(m)) klass._table = selectable klass.c = expression.ColumnCollection() mappr = mapper(klass, selectable, extension=AutoAdd(session), **mapper_kwargs) for k in mappr.iterate_properties: klass.c[k.key] = k.columns[0] klass._query = session.query_property() return klass class SQLSoup(object): """Represent an ORM-wrapped database resource.""" def __init__(self, engine_or_metadata, base=object, session=None): """Initialize a new :class:`.SQLSoup`. :param engine_or_metadata: a string database URL, :class:`.Engine` or :class:`.MetaData` object to associate with. If the argument is a :class:`.MetaData`, it should be *bound* to an :class:`.Engine`. :param base: a class which will serve as the default class for returned mapped classes. Defaults to ``object``. :param session: a :class:`.ScopedSession` or :class:`.Session` with which to associate ORM operations for this :class:`.SQLSoup` instance. If ``None``, a :class:`.ScopedSession` that's local to this module is used. """ self.session = session or Session self.base = base if isinstance(engine_or_metadata, MetaData): self._metadata = engine_or_metadata elif isinstance(engine_or_metadata, string_types + (Engine, )): self._metadata = MetaData(engine_or_metadata) else: raise ArgumentError("invalid engine or metadata argument %r" % engine_or_metadata) self._cache = {} self.schema = None @property def bind(self): """The :class:`sqlalchemy.engine.base.Engine` associated with this :class:`.SQLSoup`.""" return self._metadata.bind engine = bind def delete(self, instance): """Mark an instance as deleted.""" self.session.delete(instance) def execute(self, stmt, **params): """Execute a SQL statement. The statement may be a string SQL string, an :func:`sqlalchemy.sql.expression.select` construct, or a :func:`sqlalchemy.sql.expression.text` construct. """ return self.session.execute(sql.text(stmt, bind=self.bind), **params) @property def _underlying_session(self): if isinstance(self.session, session.Session): return self.session else: return self.session() def connection(self): """Return the current :class:`sqlalchemy.engine.base.Connection` in use by the current transaction.""" return self._underlying_session._connection_for_bind(self.bind) def flush(self): """Flush pending changes to the database. See :meth:`sqlalchemy.orm.session.Session.flush`. """ self.session.flush() def rollback(self): """Rollback the current transction. See :meth:`sqlalchemy.orm.session.Session.rollback`. """ self.session.rollback() def commit(self): """Commit the current transaction. See :meth:`sqlalchemy.orm.session.Session.commit`. """ self.session.commit() def expunge(self, instance): """Remove an instance from the :class:`.Session`. See :meth:`sqlalchemy.orm.session.Session.expunge`. """ self.session.expunge(instance) def expunge_all(self): """Clear all objects from the current :class:`.Session`. See :meth:`.Session.expunge_all`. """ self.session.expunge_all() def map_to(self, attrname, tablename=None, selectable=None, schema=None, base=None, mapper_args=util.immutabledict()): """Configure a mapping to the given attrname. This is the "master" method that can be used to create any configuration. :param attrname: String attribute name which will be established as an attribute on this :class:.`.SQLSoup` instance. :param base: a Python class which will be used as the base for the mapped class. If ``None``, the "base" argument specified by this :class:`.SQLSoup` instance's constructor will be used, which defaults to ``object``. :param mapper_args: Dictionary of arguments which will be passed directly to :func:`.orm.mapper`. :param tablename: String name of a :class:`.Table` to be reflected. If a :class:`.Table` is already available, use the ``selectable`` argument. This argument is mutually exclusive versus the ``selectable`` argument. :param selectable: a :class:`.Table`, :class:`.Join`, or :class:`.Select` object which will be mapped. This argument is mutually exclusive versus the ``tablename`` argument. :param schema: String schema name to use if the ``tablename`` argument is present. """ if attrname in self._cache: raise SQLSoupError( "Attribute '%s' is already mapped to '%s'" % ( attrname, class_mapper(self._cache[attrname]).mapped_table )) if tablename is not None: if not isinstance(tablename, string_types): raise ArgumentError("'tablename' argument must be a string." ) if selectable is not None: raise ArgumentError("'tablename' and 'selectable' " "arguments are mutually exclusive") selectable = Table(tablename, self._metadata, autoload=True, autoload_with=self.bind, schema=schema or self.schema) elif schema: raise ArgumentError("'tablename' argument is required when " "using 'schema'.") elif selectable is not None: if not isinstance(selectable, expression.FromClause): raise ArgumentError("'selectable' argument must be a " "table, select, join, or other " "selectable construct.") else: raise ArgumentError("'tablename' or 'selectable' argument is " "required.") if not selectable.primary_key.columns: if tablename: raise SQLSoupError( "table '%s' does not have a primary " "key defined" % tablename) else: raise SQLSoupError( "selectable '%s' does not have a primary " "key defined" % selectable) mapped_cls = _class_for_table( self.session, self.engine, selectable, base or self.base, mapper_args ) self._cache[attrname] = mapped_cls return mapped_cls def map(self, selectable, base=None, **mapper_args): """Map a selectable directly. The class and its mapping are not cached and will be discarded once dereferenced (as of 0.6.6). :param selectable: an :func:`.expression.select` construct. :param base: a Python class which will be used as the base for the mapped class. If ``None``, the "base" argument specified by this :class:`.SQLSoup` instance's constructor will be used, which defaults to ``object``. :param mapper_args: Dictionary of arguments which will be passed directly to :func:`.orm.mapper`. """ return _class_for_table( self.session, self.engine, selectable, base or self.base, mapper_args ) def with_labels(self, selectable, base=None, **mapper_args): """Map a selectable directly, wrapping the selectable in a subquery with labels. The class and its mapping are not cached and will be discarded once dereferenced (as of 0.6.6). :param selectable: an :func:`.expression.select` construct. :param base: a Python class which will be used as the base for the mapped class. If ``None``, the "base" argument specified by this :class:`.SQLSoup` instance's constructor will be used, which defaults to ``object``. :param mapper_args: Dictionary of arguments which will be passed directly to :func:`.orm.mapper`. """ # TODO give meaningful aliases return self.map( expression._clause_element_as_expr(selectable). select(use_labels=True). alias('foo'), base=base, **mapper_args) def join(self, left, right, onclause=None, isouter=False, base=None, **mapper_args): """Create an :func:`.expression.join` and map to it. The class and its mapping are not cached and will be discarded once dereferenced (as of 0.6.6). :param left: a mapped class or table object. :param right: a mapped class or table object. :param onclause: optional "ON" clause construct.. :param isouter: if True, the join will be an OUTER join. :param base: a Python class which will be used as the base for the mapped class. If ``None``, the "base" argument specified by this :class:`.SQLSoup` instance's constructor will be used, which defaults to ``object``. :param mapper_args: Dictionary of arguments which will be passed directly to :func:`.orm.mapper`. """ j = join(left, right, onclause=onclause, isouter=isouter) return self.map(j, base=base, **mapper_args) def entity(self, attr, schema=None): """Return the named entity from this :class:`.SQLSoup`, or create if not present. For more generalized mapping, see :meth:`.map_to`. """ try: return self._cache[attr] except KeyError: return self.map_to(attr, tablename=attr, schema=schema) def __getattr__(self, attr): return self.entity(attr) def __repr__(self): return 'SQLSoup(%r)' % self._metadata sqlsoup-0.9.1/MANIFEST.in0000644000175000017500000000032212241255461016050 0ustar classicclassic00000000000000recursive-include docs *.html *.css *.txt *.js *.jpg *.png *.py Makefile *.rst *.sty recursive-include tests *.py *.dat include README* LICENSE distribute_setup.py CHANGES* test.cfg prune docs/build/output sqlsoup-0.9.1/PKG-INFO0000664000175000017500000000331112654224057015417 0ustar classicclassic00000000000000Metadata-Version: 1.1 Name: sqlsoup Version: 0.9.1 Summary: A one step database access tool, built on the SQLAlchemy ORM. Home-page: http://bitbucket.org/zzzeek/sqlsoup Author: Mike Bayer Author-email: mike@zzzcomputing.com License: MIT Description: ======= SQLSoup ======= SQLSoup provides a convenient way to map Python objects to relational database tables, with no declarative code of any kind. It's built on top of the `SQLAlchemy `_ ORM and provides a super-minimalistic interface to an existing database. Usage is as simple as:: import sqlsoup db = sqlsoup.SQLSoup("postgresql://scott:tiger@localhost/test") for user in db.users.all(): print "user:", user.name db.users.filter_by(name="ed").update({"name":"jack"}) db.commit() Included for many years as an extension to SQLAlchemy itself, SQLSoup has been broken out into it's own project as of 2012. The community is encouraged to collaborate on Bitbucket with patches and features. Documentation and status of SQLSoup is at http://readthedocs.org/docs/sqlsoup/. Keywords: SQLAlchemy ORM Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Database :: Front-Ends sqlsoup-0.9.1/LICENSE0000644000175000017500000000221212241255461015317 0ustar classicclassic00000000000000This is the MIT license: http://www.opensource.org/licenses/mit-license.php Copyright (C) 2005-2012 by Jonathan Ellis and Michael Bayer. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sqlsoup-0.9.1/docs/0000775000175000017500000000000012654224057015254 5ustar classicclassic00000000000000sqlsoup-0.9.1/docs/build/0000775000175000017500000000000012654224057016353 5ustar classicclassic00000000000000sqlsoup-0.9.1/docs/build/tutorial.rst0000644000175000017500000003531312241255461020746 0ustar classicclassic00000000000000========= Tutorial ========= SQLSoup provides a convenient way to access existing database tables without having to declare table or mapper classes ahead of time. It is built on top of the SQLAlchemy ORM and provides a super-minimalistic interface to an existing database. SQLSoup effectively provides a coarse grained, alternative interface to working with the SQLAlchemy ORM, providing a "self configuring" interface for extremely rudimental operations. It's somewhat akin to a "super novice mode" version of the ORM. While you can do a lot more with the SQLAlchemy ORM directly, SQLSoup will have you querying an existing database in just two lines of code. SQLSoup is really well suited to quick one-offs, early learning of SQLAlchemy, and small scripting activities. It can be used in larger applications such as web applications as well, but here you'll begin to experience diminishing returns; in a substantial web application, it might be time to just switch to SQLAlchemy's :ref:`ormtutorial_toplevel`. Getting Ready to Connect ========================= Suppose we have a database with users, books, and loans tables (corresponding to the PyWebOff dataset, if you're curious). Creating a SQLSoup gateway is just like creating a SQLAlchemy engine. The urls are in the same format as those used by :func:`sqlalchemy.create_engine`:: >>> import sqlsoup >>> db = sqlsoup.SQLSoup('postgresql://scott:tiger@localhost/test') or, you can re-use an existing engine:: >>> db = sqlsoup.SQLSoup(engine) You can optionally specify a schema within the database for your SQLSoup:: >>> db.schema = "myschemaname" Note that the :class:`.SQLSoup` object doesn't actually connect to the database until it's first asked to do something. If the connection string is incorrect, the error will be raised when SQLSoup first tries to connect. See also: :ref:`engines_toplevel` - SQLAlchemy supported database backends, engine connect strings, etc. Loading objects =============== When using a :class:`.SQLSoup`, you access attributes from it which match the name of a table in the database. If your database has a table named ``users``, you'd get to it via an attribute named ``.users``. Loading objects is as easy as this:: >>> users = db.users.all() >>> users.sort() >>> users [ MappedUsers(name=u'Joe Student',email=u'student@example.edu', password=u'student',classname=None,admin=0), MappedUsers(name=u'Bhargan Basepair',email=u'basepair@example.edu', password=u'basepair',classname=None,admin=1) ] Field access is intuitive:: >>> users[0].email u'student@example.edu' An alternative to ``db.users`` is to use the :meth:`.SQLSoup.entity` method, which accepts a string argument. This is useful if the name of your table has special casing or other character considerations: >>> my_user_table = db.entity("User_Table") You can then refer to ``my_user_table`` the same way we refer to ``db.users`` in this tutorial. Basic Table Usage ================= The table object proxies out to the SQLAlchemy :class:`sqlalchemy.orm.query.Query` object. For example, we can sort with ``order_by()``:: >>> db.users.order_by(db.users.name).all() [ MappedUsers(name=u'Bhargan Basepair',email=u'basepair@example.edu', password=u'basepair',classname=None,admin=1), MappedUsers(name=u'Joe Student',email=u'student@example.edu', password=u'student',classname=None,admin=0) ] Of course, you don't want to load all users very often. Let's add a WHERE clause. Let's also switch the order_by to DESC while we're at it:: >>> from sqlalchemy import or_, and_, desc >>> where = or_(db.users.name=='Bhargan Basepair', db.users.email=='student@example.edu') >>> db.users.filter(where).order_by(desc(db.users.name)).all() [ MappedUsers(name=u'Joe Student',email=u'student@example.edu', password=u'student',classname=None,admin=0), MappedUsers(name=u'Bhargan Basepair',email=u'basepair@example.edu', password=u'basepair',classname=None,admin=1) ] You can also use .first() (to retrieve only the first object from a query) or .one() (like .first when you expect exactly one user -- it will raise an exception if more were returned):: >>> db.users.filter(db.users.name=='Bhargan Basepair').one() MappedUsers(name=u'Bhargan Basepair',email=u'basepair@example.edu', password=u'basepair',classname=None,admin=1) Since name is the primary key, this is equivalent to >>> db.users.get('Bhargan Basepair') MappedUsers(name=u'Bhargan Basepair',email=u'basepair@example.edu', password=u'basepair',classname=None,admin=1) This is also equivalent to >>> db.users.filter_by(name='Bhargan Basepair').one() MappedUsers(name=u'Bhargan Basepair',email=u'basepair@example.edu', password=u'basepair',classname=None,admin=1) filter_by is like filter, but takes kwargs instead of full clause expressions. This makes it more concise for simple queries like this, but you can't do complex queries like the or\_ above or non-equality based comparisons this way. Full query documentation ------------------------ Get, filter, filter_by, order_by, limit, and the rest of the query methods are explained in detail in :ref:`ormtutorial_querying`. Modifying objects ================= Modifying objects is intuitive:: >>> user.email = 'basepair+nospam@example.edu' >>> db.commit() (SQLSoup leverages the sophisticated SQLAlchemy unit-of-work code, so multiple updates to a single object will be turned into a single ``UPDATE`` statement when you commit.) To finish covering the basics, let's insert a new loan, then delete it:: >>> book_id = db.books.filter_by(title='Regional Variation in Moss').first().id >>> db.loans.insert(book_id=book_id, user_name=user.name) MappedLoans(book_id=2,user_name=u'Bhargan Basepair',loan_date=None) >>> loan = db.loans.filter_by(book_id=2, user_name='Bhargan Basepair').one() >>> db.delete(loan) >>> db.commit() You can also delete rows that have not been loaded as objects. Let's do our insert/delete cycle once more, this time using the loans table's delete method. (For SQLAlchemy experts: note that no flush() call is required since this delete acts at the SQL level, not at the Mapper level.) The same where-clause construction rules apply here as to the select methods:: >>> db.loans.insert(book_id=book_id, user_name=user.name) MappedLoans(book_id=2,user_name=u'Bhargan Basepair',loan_date=None) >>> db.loans.delete(db.loans.book_id==2) You can similarly update multiple rows at once. This will change the book_id to 1 in all loans whose book_id is 2:: >>> db.loans.filter_by(db.loans.book_id==2).update({'book_id':1}) >>> db.loans.filter_by(book_id=1).all() [MappedLoans(book_id=1,user_name=u'Joe Student', loan_date=datetime.datetime(2006, 7, 12, 0, 0))] Joins ===== Occasionally, you will want to pull out a lot of data from related tables all at once. In this situation, it is far more efficient to have the database perform the necessary join. (Here we do not have *a lot of data* but hopefully the concept is still clear.) SQLAlchemy is smart enough to recognize that loans has a foreign key to users, and uses that as the join condition automatically:: >>> join1 = db.join(db.users, db.loans, isouter=True) >>> join1.filter_by(name='Joe Student').all() [ MappedJoin(name=u'Joe Student',email=u'student@example.edu', password=u'student',classname=None,admin=0,book_id=1, user_name=u'Joe Student',loan_date=datetime.datetime(2006, 7, 12, 0, 0)) ] If you're unfortunate enough to be using MySQL with the default MyISAM storage engine, you'll have to specify the join condition manually, since MyISAM does not store foreign keys. Here's the same join again, with the join condition explicitly specified:: >>> db.join(db.users, db.loans, db.users.name==db.loans.user_name, isouter=True) You can compose arbitrarily complex joins by combining Join objects with tables or other joins. Here we combine our first join with the books table:: >>> join2 = db.join(join1, db.books) >>> join2.all() [ MappedJoin(name=u'Joe Student',email=u'student@example.edu', password=u'student',classname=None,admin=0,book_id=1, user_name=u'Joe Student',loan_date=datetime.datetime(2006, 7, 12, 0, 0), id=1,title=u'Mustards I Have Known',published_year=u'1989', authors=u'Jones') ] If you join tables that have an identical column name, wrap your join with `with_labels`, to disambiguate columns with their table name (.c is short for .columns):: >>> db.with_labels(join1).c.keys() [u'users_name', u'users_email', u'users_password', u'users_classname', u'users_admin', u'loans_book_id', u'loans_user_name', u'loans_loan_date'] You can also join directly to a labeled object:: >>> labeled_loans = db.with_labels(db.loans) >>> db.join(db.users, labeled_loans, isouter=True).c.keys() [u'name', u'email', u'password', u'classname', u'admin', u'loans_book_id', u'loans_user_name', u'loans_loan_date'] Relationships ============= You can define relationships between classes using the :meth:`~.TableClassType.relate` method from any mapped table: >>> db.users.relate('loans', db.loans) These can then be used like a normal SA property: >>> db.users.get('Joe Student').loans [MappedLoans(book_id=1,user_name=u'Joe Student', loan_date=datetime.datetime(2006, 7, 12, 0, 0))] >>> db.users.filter(~db.users.loans.any()).all() [MappedUsers(name=u'Bhargan Basepair', email='basepair+nospam@example.edu', password=u'basepair',classname=None,admin=1)] :meth:`~.TableClassType.relate` can take any options that the relationship function accepts in normal mapper definition: >>> del db._cache['users'] >>> db.users.relate('loans', db.loans, order_by=db.loans.loan_date, cascade='all, delete-orphan') Advanced Use ============ Sessions, Transactions and Application Integration -------------------------------------------------- .. note:: Please read and understand this section thoroughly before using SQLSoup in any web application. SQLSoup uses a :class:`sqlalchemy.orm.scoping.ScopedSession` to provide thread-local sessions. You can get a reference to the current one like this:: >>> session = db.session The default session is available at the module level in SQLSoup, via:: >>> from sqlsoup import Session The configuration of this session is ``autoflush=True``, ``autocommit=False``. This means when you work with the SQLSoup object, you need to call :meth:`.SQLSoup.commit` in order to have changes persisted. You may also call :meth:`.SQLSoup.rollback` to roll things back. Since the SQLSoup object's Session automatically enters into a transaction as soon as it's used, it is *essential* that you call :meth:`.SQLSoup.commit` or :meth:`.SQLSoup.rollback` on it when the work within a thread completes. This means all the guidelines for web application integration at :ref:`session_lifespan` must be followed. The SQLSoup object can have any session or scoped session configured onto it. This is of key importance when integrating with existing code or frameworks such as Pylons. If your application already has a ``Session`` configured, pass it to your SQLSoup object:: >>> from myapplication import Session >>> db = SQLSoup(session=Session) If the ``Session`` is configured with ``autocommit=True``, use ``flush()`` instead of ``commit()`` to persist changes - in this case, the ``Session`` closes out its transaction immediately and no external management is needed. ``rollback()`` is also not available. Configuring a new SQLSoup object in "autocommit" mode looks like:: >>> from sqlalchemy.orm import scoped_session, sessionmaker >>> db = SQLSoup('sqlite://', session=scoped_session(sessionmaker(autoflush=False, expire_on_commit=False, autocommit=True))) Mapping arbitrary Selectables ----------------------------- SQLSoup can map any SQLAlchemy :class:`sqlalchemy.sql.expression.Selectable` with the map method. Let's map an :func:`sqlalchemy.sql.expression.select` object that uses an aggregate function; we'll use the SQLAlchemy :class:`sqlalchemy.schema.Table` that SQLSoup introspected as the basis. (Since we're not mapping to a simple table or join, we need to tell SQLAlchemy how to find the *primary key* which just needs to be unique within the select, and not necessarily correspond to a *real* PK in the database.):: >>> from sqlalchemy import select, func >>> b = db.books._table >>> s = select([b.c.published_year, func.count('*').label('n')], from_obj=[b], group_by=[b.c.published_year]) >>> s = s.alias('years_with_count') >>> years_with_count = db.map(s, primary_key=[s.c.published_year]) >>> years_with_count.filter_by(published_year='1989').all() [MappedBooks(published_year=u'1989',n=1)] Obviously if we just wanted to get a list of counts associated with book years once, raw SQL is going to be less work. The advantage of mapping a Select is reusability, both standalone and in Joins. (And if you go to full SQLAlchemy, you can perform mappings like this directly to your object models.) An easy way to save mapped selectables like this is to just hang them on your db object:: >>> db.years_with_count = years_with_count Python is flexible like that! Raw SQL ------- SQLSoup works fine with SQLAlchemy's text construct, described in :ref:`sqlexpression_text`. You can also execute textual SQL directly using the :meth:`.SQLSoup.execute` method, which corresponds to the :meth:`sqlalchemy.orm.session.Session.execute` method on the underlying :class:`sqlalchemy.orm.session.Session`. Expressions here are expressed like :func:`sqlalchemy.sql.expression.text` constructs, using named parameters with colons:: >>> rp = db.execute('select name, email from users where name like :name order by name', name='%Bhargan%') >>> for name, email in rp.fetchall(): print name, email Bhargan Basepair basepair+nospam@example.edu Or you can get at the current transaction's connection using :meth:`.SQLSoup.connection`. This is the raw connection object which can accept any sort of SQL expression or raw SQL string passed to the database:: >>> conn = db.connection() >>> conn.execute("'select name, email from users where name like ? order by name'", '%Bhargan%') Dynamic table names ------------------- You can load a table whose name is specified at runtime with the :meth:`.SQLSoup.entity` method: >>> tablename = 'loans' >>> db.entity(tablename) is db.loans True :meth:`.SQLSoup.entity` also takes an optional schema argument. If none is specified, the default schema is used. sqlsoup-0.9.1/docs/build/requirements.txt0000644000175000017500000000001312241256656021631 0ustar classicclassic00000000000000changelog sqlsoup-0.9.1/docs/build/api.rst0000644000175000017500000000023412241255461017646 0ustar classicclassic00000000000000=========== API Details =========== This section includes generated documentation for the :class:`.SQLSoup` object. .. automodule:: sqlsoup :members: sqlsoup-0.9.1/docs/build/Makefile0000644000175000017500000001316212241255461020007 0ustar classicclassic00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = output # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dist-html same as html, but places files in /docs" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dist-html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .. @echo @echo "Build finished. The HTML pages are in ../." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SQLSoup.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SQLSoup.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/SQLSoup" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SQLSoup" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." sqlsoup-0.9.1/docs/build/conf.py0000644000175000017500000001773612241256635017665 0ustar classicclassic00000000000000# -*- coding: utf-8 -*- # # SQLSoup documentation build configuration file, created by # sphinx-quickstart on Sun Jan 8 12:07:16 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath('../../')) import sqlsoup # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'changelog'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'SQLSoup' copyright = u'2005-2012, Jonathan Ellis, Mike Bayer' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = sqlsoup.__version__ # The full version, including alpha/beta/rc tags. release = sqlsoup.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'SQLSoupdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'SQLSoup.tex', u'SQLSoup Documentation', u'Jonathan Ellis, Mike Bayer', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'sqlsoup', u'SQLSoup Documentation', [u'Jonathan Ellis, Mike Bayer'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'SQLSoup', u'SQLSoup Documentation', u'Jonathan Ellis, Mike Bayer', 'SQLSoup', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} intersphinx_mapping = {'sqla':('http://www.sqlalchemy.org/docs/', None)} sqlsoup-0.9.1/docs/build/index.rst0000644000175000017500000000063312241256027020206 0ustar classicclassic00000000000000Welcome to SQLSoup's documentation! =================================== `SQLSoup `_ is a one step database access tool built on top of the `SQLAlchemy `_ Object Relational Mapper. .. toctree:: :maxdepth: 2 front tutorial api changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sqlsoup-0.9.1/docs/build/changelog.rst0000644000175000017500000000073212654223714021033 0ustar classicclassic00000000000000============== Changelog ============== .. changelog:: :version: 0.9.1 :released: Tue Feb 2 2016 .. change:: :tags: bug :tickets: 1 Fixed issue where errant "py2k" comment was present, replaced with a compat conditional. .. change:: :tags: feature Removed the need for the 2to3 tool on Python 3. .. changelog:: :version: 0.9.0 :released: Sun Jan 8 2012 .. change:: :tags: Initial release. sqlsoup-0.9.1/docs/build/front.rst0000644000175000017500000000333312241255461020230 0ustar classicclassic00000000000000============ Front Matter ============ Information about the SQLSoup project. Project Homepage ================ SQLSoup is hosted on `Bitbucket `_ - the lead project page is at https://bitbucket.org/zzzeek/sqlsoup. Source code is tracked here using `Mercurial `_. Releases and project status are available on Pypi at http://pypi.python.org/pypi/sqlsoup. The most recent published version of this documentation should be at http://readthedocs.org/docs/sqlsoup/. Project Status ============== SQLSoup has been included with the SQLAlchemy distribution for many years. As of 2012 it has been broken out into it's own project, where it can be tracked and released separately. The community is also invited to collaborate on Bitbucket, create forks, and improve the product. .. _installation: Installation ============ Install released versions of SQLSoup from the Python package index with `pip `_ or a similar tool:: pip install sqlsoup Installation via source distribution is via the ``setup.py`` script:: python setup.py install Dependencies ------------ SQLSoup's install process will ensure that `SQLAlchemy `_ is installed, in addition to other dependencies. The 0.7 series of SQLAlchemy or greater is recommended. Community ========= SQLSoup was originally written by Jonathan Ellis. It is under maintenance by `Mike Bayer `_, and is loosely associated with the `SQLAlchemy`_ project. .. _bugs: Bugs ==== Bugs and feature enhancements to SQLSoup should be reported on the `Bitbucket issue tracker `_. sqlsoup-0.9.1/setup.cfg0000664000175000017500000000007312654224057016145 0ustar classicclassic00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0