to allow ad-hoc display of the source of any file, as well as a "directory listing" structure.
- reorganize examples/ to take advantage of new extension. in particular, keep moving all
the descriptive text for files etc. into module docstrings, taking more advantage of
self-documentation.
--- /dev/null
+from docutils import nodes
+from sphinx.ext.viewcode import collect_pages
+from sphinx.pycode import ModuleAnalyzer
+import imp
+from sphinx import addnodes
+import re
+from sphinx.util.compat import Directive
+import os
+from docutils.statemachine import StringList
+from sphinx.environment import NoUri
+
+def view_source(name, rawtext, text, lineno, inliner,
+ options={}, content=[]):
+
+ env = inliner.document.settings.env
+
+ node = _view_source_node(env, text, None)
+ return [node], []
+
+def _view_source_node(env, text, state):
+ # pretend we're using viewcode fully,
+ # install the context it looks for
+ if not hasattr(env, '_viewcode_modules'):
+ env._viewcode_modules = {}
+
+ modname = text
+ text = modname.split(".")[-1] + ".py"
+
+ # imitate sphinx .<modname> syntax
+ if modname.startswith("."):
+ # see if the modname needs to be corrected in terms
+ # of current module context
+ base_module = env.temp_data.get('autodoc:module')
+ if base_module is None:
+ base_module = env.temp_data.get('py:module')
+
+ if base_module:
+ modname = base_module + modname
+
+ urito = env.app.builder.get_relative_uri
+
+ # we're showing code examples which may have dependencies
+ # which we really don't want to have required so load the
+ # module by file, not import (though we are importing)
+ # the top level module here...
+ pathname = None
+ for token in modname.split("."):
+ file_, pathname, desc = imp.find_module(token, [pathname] if pathname else None)
+ if file_:
+ file_.close()
+
+ # unlike viewcode which silently traps exceptions,
+ # I want this to totally barf if the file can't be loaded.
+ # a failed build better than a complete build missing
+ # key content
+ analyzer = ModuleAnalyzer.for_file(pathname, modname)
+ # copied from viewcode
+ analyzer.find_tags()
+ if not isinstance(analyzer.code, unicode):
+ code = analyzer.code.decode(analyzer.encoding)
+ else:
+ code = analyzer.code
+
+ if state is not None:
+ docstring = _find_mod_docstring(analyzer)
+ if docstring:
+ # get rid of "foo.py" at the top
+ docstring = re.sub(r"^[a-zA-Z_0-9]+\.py", "", docstring)
+
+ # strip
+ docstring = docstring.strip()
+
+ # yank only first paragraph
+ docstring = docstring.split("\n\n")[0].strip()
+ else:
+ docstring = None
+
+ entry = code, analyzer.tags, {}
+ env._viewcode_modules[modname] = entry
+ pagename = '_modules/' + modname.replace('.', '/')
+
+ try:
+ refuri = urito(env.docname, pagename)
+ except NoUri:
+ # if we're in the latex builder etc., this seems
+ # to be what we get
+ refuri = None
+
+
+ if docstring:
+ # embed the ref with the doc text so that it isn't
+ # a separate paragraph
+ if refuri:
+ docstring = "`%s <%s>`_ - %s" % (text, refuri, docstring)
+ else:
+ docstring = "``%s`` - %s" % (text, docstring)
+ para = nodes.paragraph('', '')
+ state.nested_parse(StringList([docstring]), 0, para)
+ return_node = para
+ else:
+ if refuri:
+ refnode = nodes.reference('', '',
+ nodes.Text(text, text),
+ refuri=urito(env.docname, pagename)
+ )
+ else:
+ refnode = nodes.Text(text, text)
+
+ if state:
+ return_node = nodes.paragraph('', '', refnode)
+ else:
+ return_node = refnode
+
+ return return_node
+
+from sphinx.pycode.pgen2 import token
+
+def _find_mod_docstring(analyzer):
+ """attempt to locate the module-level docstring.
+
+ Note that sphinx autodoc just uses ``__doc__``. But we don't want
+ to import the module, so we need to parse for it.
+
+ """
+ analyzer.tokenize()
+ for type_, parsed_line, start_pos, end_pos, raw_line in analyzer.tokens:
+ if type_ == token.COMMENT:
+ continue
+ elif type_ == token.STRING:
+ return eval(parsed_line)
+ else:
+ return None
+
+def _parse_content(content):
+ d = {}
+ d['text'] = []
+ idx = 0
+ for line in content:
+ idx += 1
+ m = re.match(r' *\:(.+?)\:(?: +(.+))?', line)
+ if m:
+ attrname, value = m.group(1, 2)
+ d[attrname] = value or ''
+ else:
+ break
+ d["text"] = content[idx:]
+ return d
+
+def _comma_list(text):
+ return re.split(r"\s*,\s*", text.strip())
+
+class AutoSourceDirective(Directive):
+ has_content = True
+
+ def run(self):
+ content = _parse_content(self.content)
+
+
+ env = self.state.document.settings.env
+ self.docname = env.docname
+
+ sourcefile = self.state.document.current_source.split(":")[0]
+ dir_ = os.path.dirname(sourcefile)
+ files = [
+ f for f in os.listdir(dir_) if f.endswith(".py")
+ and f != "__init__.py"
+ ]
+
+ if "files" in content:
+ # ordered listing of files to include
+ files = [fname for fname in _comma_list(content["files"])
+ if fname in set(files)]
+
+ node = nodes.paragraph('', '',
+ nodes.Text("Listing of files:", "Listing of files:")
+ )
+
+ bullets = nodes.bullet_list()
+ for fname in files:
+ modname, ext = os.path.splitext(fname)
+ # relative lookup
+ modname = "." + modname
+
+ link = _view_source_node(env, modname, self.state)
+
+ list_node = nodes.list_item('',
+ link
+ )
+ bullets += list_node
+
+ node += bullets
+
+ return [node]
+
+def setup(app):
+ app.add_role('viewsource', view_source)
+
+ app.add_directive('autosource', AutoSourceDirective)
+
+ # from sphinx.ext.viewcode
+ app.connect('html-collect-pages', collect_pages)
# 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('../../lib'))
-sys.path.insert(0, os.path.abspath('../../examples'))
+sys.path.insert(0, os.path.abspath('../..')) # examples
sys.path.insert(0, os.path.abspath('.'))
import sqlalchemy
'builder.dialect_info',
'builder.mako',
'builder.sqlformatter',
+ 'builder.viewsource',
]
# Add any paths that contain templates here, relative to this directory.
Additional SQLAlchemy examples, some user contributed, are available on the
wiki at `<http://www.sqlalchemy.org/trac/wiki/UsageRecipes>`_.
+
.. _examples_adjacencylist:
Adjacency List
--------------
-Location: /examples/adjacency_list/
-
-.. automodule:: adjacency_list
+.. automodule:: examples.adjacency_list
.. _examples_associations:
Associations
------------
-Location: /examples/association/
-
-.. automodule:: association
+.. automodule:: examples.association
.. _examples_instrumentation:
Attribute Instrumentation
-------------------------
-Location: /examples/custom_attributes/
-
-.. automodule:: custom_attributes
+.. automodule:: examples.custom_attributes
.. _examples_caching:
Dogpile Caching
---------------
-Location: /examples/dogpile_caching/
-
-.. automodule:: dogpile_caching
+.. automodule:: examples.dogpile_caching
Directed Graphs
---------------
-Location: /examples/graphs/
-
-.. automodule:: graphs
+.. automodule:: examples.graphs
Dynamic Relations as Dictionaries
----------------------------------
-Location: /examples/dynamic_dict/
-
-.. automodule:: dynamic_dict
+.. automodule:: examples.dynamic_dict
.. _examples_generic_associations:
Generic Associations
--------------------
-Location: /examples/generic_associations
-
-.. automodule:: generic_associations
+.. automodule:: examples.generic_associations
.. _examples_sharding:
Horizontal Sharding
-------------------
-Location: /examples/sharding
-
-.. automodule:: sharding
+.. automodule:: examples.sharding
Inheritance Mappings
--------------------
-Location: /examples/inheritance/
-
-.. automodule:: inheritance
+.. automodule:: examples.inheritance
Large Collections
-----------------
-Location: /examples/large_collection/
-
-.. automodule:: large_collection
+.. automodule:: examples.large_collection
Nested Sets
-----------
-Location: /examples/nested_sets/
-
-.. automodule:: nested_sets
-
-Polymorphic Associations
-------------------------
-
-See :ref:`examples_generic_associations` for a modern version of polymorphic associations.
+.. automodule:: examples.nested_sets
.. _examples_postgis:
PostGIS Integration
-------------------
-Location: /examples/postgis
-
-.. automodule:: postgis
+.. automodule:: examples.postgis
Versioned Objects
-----------------
-Location: /examples/versioning
-
-.. automodule:: versioning
+.. automodule:: examples.versioning
Vertical Attribute Mapping
--------------------------
-Location: /examples/vertical
-
-.. automodule:: vertical
+.. automodule:: examples.vertical
.. _examples_xmlpersistence:
XML Persistence
---------------
-Location: /examples/elementtree/
-
-.. automodule:: elementtree
+.. automodule:: examples.elementtree
dump_tree(node)
+.. autosource::
+
"""
"""
-
Examples illustrating the usage of the "association object" pattern,
where an intermediary class mediates the relationship between two
classes that are associated in a many-to-many pattern.
-This directory includes the following examples:
-
-* basic_association.py - illustrate a many-to-many relationship between an
- "Order" and a collection of "Item" objects, associating a purchase price
- with each via an association object called "OrderItem"
-* proxied_association.py - same example as basic_association, adding in
- usage of :mod:`sqlalchemy.ext.associationproxy` to make explicit references
- to "OrderItem" optional.
-* dict_of_sets_with_default.py - an advanced association proxy example which
- illustrates nesting of association proxies to produce multi-level Python
- collections, in this case a dictionary with string keys and sets of integers
- as values, which conceal the underlying mapped classes.
+.. autosource::
"""
\ No newline at end of file
-"""A basic example of using the association object pattern.
+"""basic_association.py
+
+illustrate a many-to-many relationship between an
+"Order" and a collection of "Item" objects, associating a purchase price
+with each via an association object called "OrderItem"
The association object pattern is a form of many-to-many which
associates additional data with each association between parent/child.
-"""Illustrate a 'dict of sets of integers' model.
+"""dict_of_sets_with_default.py
+
+an advanced association proxy example which
+illustrates nesting of association proxies to produce multi-level Python
+collections, in this case a dictionary with string keys and sets of integers
+as values, which conceal the underlying mapped classes.
This is a three table model which represents a parent table referencing a
dictionary of string keys and sets as values, where each set stores a
-"""An extension to the basic_association.py example, which illustrates
-the usage of sqlalchemy.ext.associationproxy.
+"""proxied_association.py
+
+same example as basic_association, adding in
+usage of :mod:`sqlalchemy.ext.associationproxy` to make explicit references
+to ``OrderItem`` optional.
+
"""
Two examples illustrating modifications to SQLAlchemy's attribute management
system.
-``listen_for_events.py`` illustrates the usage of
-:class:`~sqlalchemy.orm.interfaces.AttributeExtension` to intercept attribute
-events. It additionally illustrates a way to automatically attach these
-listeners to all class attributes using a
-:class:`.InstrumentationManager`.
-
-``custom_management.py`` illustrates much deeper usage of
-:class:`.InstrumentationManager` as well as
-collection adaptation, to completely change the underlying method used to
-store state on an object. This example was developed to illustrate
-techniques which would be used by other third party object instrumentation
-systems to interact with SQLAlchemy's event system and is only intended for
-very intricate framework integrations.
+.. autosource::
"""
\ No newline at end of file
python examples/dogpile_caching/local_session_caching.py
-
-Listing of files:
-
- environment.py - Establish the Session, a dictionary
- of "regions", a sample cache region against a .dbm
- file, data / cache file paths, and configurations,
- bootstrap fixture data if necessary.
-
- caching_query.py - Represent functions and classes
- which allow the usage of Dogpile caching with SQLAlchemy.
- Introduces a query option called FromCache.
-
- model.py - The datamodel, which represents Person that has multiple
- Address objects, each with PostalCode, City, Country
-
- fixture_data.py - creates demo PostalCode, Address, Person objects
- in the database.
-
- helloworld.py - the basic idea.
-
- relationship_caching.py - Illustrates how to add cache options on
- relationship endpoints, so that lazyloads load from cache.
-
- advanced.py - Further examples of how to use FromCache. Combines
- techniques from the first two scripts.
-
- local_session_caching.py - Grok everything so far ? This example
- creates a new dogpile.cache backend that will persist data in a dictionary
- which is local to the current session. remove() the session
- and the cache is gone.
+.. autosource::
+ :files: environment.py, caching_query.py, model.py, fixture_data.py, \
+ helloworld.py, relationship_caching.py, advanced.py, \
+ local_session_caching.py
"""
"""caching_query.py
-Represent persistence structures which allow the usage of
-dogpile.cache caching with SQLAlchemy.
+Represent functions and classes
+which allow the usage of Dogpile caching with SQLAlchemy.
+Introduces a query option called FromCache.
The three new concepts introduced here are:
"""local_session_caching.py
+Grok everything so far ? This example
+creates a new dogpile.cache backend that will persist data in a dictionary
+which is local to the current session. remove() the session
+and the cache is gone.
+
Create a new Dogpile cache backend that will store
cached data local to the current Session.
-"""Model. We are modeling Person objects with a collection
-of Address objects. Each Address has a PostalCode, which
-in turn references a City and then a Country:
+"""model.py
+
+The datamodel, which represents Person that has multiple
+Address objects, each with PostalCode, City, Country.
Person --(1..n)--> Address
Address --(has a)--> PostalCode
"""relationship_caching.py
+Illustrates how to add cache options on
+relationship endpoints, so that lazyloads load from cache.
+
Load a set of Person and Address objects, specifying that
related PostalCode, City, Country objects should be pulled from long
term cache.
string keys) can operate upon a large collection without loading the
full collection at once.
+.. autosource::
+
"""
\ No newline at end of file
suit any kind of DOM representation system. Querying along
xpath-like strings is illustrated as well.
-In order of complexity:
-
-* ``pickle.py`` - Quick and dirty, serialize the whole DOM into a BLOB
- column. While the example is very brief, it has very limited
- functionality.
-
-* ``adjacency_list.py`` - Each DOM node is stored in an individual
- table row, with attributes represented in a separate table. The
- nodes are associated in a hierarchy using an adjacency list
- structure. A query function is introduced which can search for nodes
- along any path with a given structure of attributes, basically a
- (very narrow) subset of xpath.
-
-* ``optimized_al.py`` - Uses the same strategy as
- ``adjacency_list.py``, but associates each DOM row with its owning
- document row, so that a full document of DOM nodes can be loaded
- using O(1) queries - the construction of the "hierarchy" is performed
- after the load in a non-recursive fashion and is much more
- efficient.
-
E.g.::
# parse an XML file and persist in the database
# dump the XML
print document
+.. autosource::
+ :files: pickle.py, adjacency_list.py, optimized_al.py
+
"""
\ No newline at end of file
-"""illustrates an explicit way to persist an XML document expressed using ElementTree.
+"""Illustrates an explicit way to persist an XML document expressed using ElementTree.
+
+Each DOM node is stored in an individual
+table row, with attributes represented in a separate table. The
+nodes are associated in a hierarchy using an adjacency list
+structure. A query function is introduced which can search for nodes
+along any path with a given structure of attributes, basically a
+(very narrow) subset of xpath.
This example explicitly marshals/unmarshals the ElementTree document into
mapped entities which have their own tables. Compare to pickle.py which
uses pickle to accomplish the same task. Note that the usage of both
styles of persistence are identical, as is the structure of the main Document class.
+
"""
################################# PART I - Imports/Coniguration ####################################
-"""This script duplicates adjacency_list.py, but optimizes the loading
-of XML nodes to be based on a "flattened" datamodel. Any number of XML documents,
-each of arbitrary complexity, can be loaded in their entirety via a single query
-which joins on only three tables.
+"""Uses the same strategy as
+ ``adjacency_list.py``, but associates each DOM row with its owning
+ document row, so that a full document of DOM nodes can be loaded
+ using O(1) queries - the construction of the "hierarchy" is performed
+ after the load in a non-recursive fashion and is more
+ efficient.
"""
parent class is provided with an ``addresses`` collection
which contains ``Address`` objects.
-The configurations include:
-
-* ``table_per_related.py`` - illustrates a distinct table per related collection.
-* ``table_per_association.py`` - illustrates a shared collection table, using a
- table per association.
-* ``discriminator_on_association.py`` - shared collection table and shared
- association table, including a discriminator column.
-* ``generic_fk.py`` - imitates the approach taken by popular frameworks such
- as Django and Ruby on Rails to create a so-called "generic foreign key".
-
-The ``discriminator_on_association.py`` and ``generic_fk.py`` scripts
+The :viewsource:`.discriminator_on_association` and :viewsource:`.generic_fk` scripts
are modernized versions of recipes presented in the 2007 blog post
`Polymorphic Associations with SQLAlchemy <http://techspot.zzzeek.org/2007/05/29/polymorphic-associations-with-sqlalchemy/>`_.
-.
+
+.. autosource::
"""
\ No newline at end of file
"""discriminator_on_related.py
-The HasAddresses mixin will provide a relationship
-to the fixed Address table based on a fixed association table.
-
-The association table contains a "discriminator"
-which determines what type of parent object associates to the
-Address row. SQLAlchemy's single-table-inheritance feature is used
+Illustrates a mixin which provides a generic association
+using a single target table and a single association table,
+referred to by all parent tables. The association table
+contains a "discriminator" column which determines what type of
+parent object associates to each particular row in the association
+table.
+
+SQLAlchemy's single-table-inheritance feature is used
to target different association types.
-This is a "polymorphic association". Even though a "discriminator"
-that refers to a particular table is present, the extra association
-table is used so that traditional foreign key constraints may be used.
-
This configuration attempts to simulate a so-called "generic foreign key"
as closely as possible without actually foregoing the use of real
foreign keys. Unlike table-per-related and table-per-association,
"""generic_fk.py
-This example will emulate key aspects of the system used by popular
-frameworks such as Django, ROR, etc.
-
-It approaches the issue by bypassing standard referential integrity
-practices, and producing a so-called "generic foreign key", which means
-a database column that is not constrained to refer to any particular table.
-In-application logic is used to determine which table is referenced.
+Illustrates a so-called "generic foreign key", in a similar fashion
+to that of popular frameworks such as Django, ROR, etc. This
+approach bypasses standard referential integrity
+practices, in that the "foreign key" column is not actually
+constrained to refer to any particular table; instead,
+in-application logic is used to determine which table is referenced.
This approach is not in line with SQLAlchemy's usual style, as foregoing
foreign key integrity means that the tables can easily contain invalid
"""table_per_association.py
-The HasAddresses mixin will provide a new "address_association" table for
-each parent class. The "address" table will be shared
-for all parents.
+Illustrates a mixin which provides a generic association
+via a individually generated association tables for each parent class.
+The associated objects themselves are persisted in a single table
+shared among all parents.
This configuration has the advantage that all Address
rows are in one table, so that the definition of "Address"
"""table_per_related.py
-The HasAddresses mixin will provide a new "address" table for
-each parent class, as well as a distinct "Address" subclass.
+Illustrates a generic association which persists association
+objects within individual tables, each one generated to persist
+those objects on behalf of a particular parent class.
This configuration has the advantage that each type of parent
maintains its "Address" rows separately, so that collection
n2.add_neighbor(n5)
print n2.higher_neighbors()
+.. autosource::
+
"""
\ No newline at end of file
"""Working examples of single-table, joined-table, and concrete-table
inheritance as described in :ref:`datamapping_inheritance`.
+.. autosource::
+
"""
\ No newline at end of file
+"""Concrete (table-per-class) inheritance example."""
+
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, \
String
from sqlalchemy.orm import mapper, sessionmaker, polymorphic_union
-"""this example illustrates a polymorphic load of two classes"""
+"""Joined-table (table-per-subclass) inheritance example."""
from sqlalchemy import Table, Column, Integer, String, \
ForeignKey, create_engine, inspect, or_
+"""Single-table inheritance example."""
+
from sqlalchemy import MetaData, Table, Column, Integer, String, \
ForeignKey, create_engine
from sqlalchemy.orm import mapper, relationship, sessionmaker
``passive_deletes=True`` to greatly improve the performance of
related collection deletion.
+.. autosource::
+
"""
""" Illustrates a rudimentary way to implement the "nested sets"
pattern for hierarchical data using the SQLAlchemy ORM.
+.. autosource::
+
"""
\ No newline at end of file
print session.query(Road).filter(Road.road_geom.intersects(r1.road_geom)).all()
+.. autosource::
+
"""
database nodes) in an explicit way - described on the wiki at
`EntityName <http://www.sqlalchemy.org/trac/wiki/UsageRecipes/EntityName>`_.
+.. autosource::
+
"""
SomeHistoryClass = SomeClass.__history_mapper__.class_
+.. autosource::
+
"""
\ No newline at end of file
+"""Versioned mixin class and other utilities."""
+
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import mapper, class_mapper, attributes, object_mapper
from sqlalchemy.orm.exc import UnmappedClassError, UnmappedColumnError
+"""Unit tests illustrating usage of the ``history_meta.py`` module functions."""
+
from unittest import TestCase
from sqlalchemy.ext.declarative import declarative_base
from .history_meta import Versioned, versioned_session
AnimalFact.value == True))))
print 'weasel-like animals', q.all()
+.. autosource::
"""
\ No newline at end of file