From 8e3580bda6f6060aed3bd1c80ad9023390480d0b Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Sat, 5 Mar 2011 05:40:36 +0800 Subject: [PATCH] putting doctests in a different package --- isis/model/mapper.py | 75 +--------------------- isis/model/ordered.py | 51 --------------- isis/model/subfield.py | 101 ++++-------------------------- isis/model/tests/__init__.py | 0 isis/model/tests/test_mapper.py | 83 ++++++++++++++++++++++++ isis/model/tests/test_ordered.py | 61 ++++++++++++++++++ isis/model/tests/test_subfield.py | 98 +++++++++++++++++++++++++++++ setup.py | 20 +++--- 8 files changed, 267 insertions(+), 222 deletions(-) create mode 100644 isis/model/tests/__init__.py create mode 100644 isis/model/tests/test_mapper.py create mode 100644 isis/model/tests/test_ordered.py create mode 100644 isis/model/tests/test_subfield.py diff --git a/isis/model/mapper.py b/isis/model/mapper.py index 68a835c..e020653 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -18,81 +18,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -""" ----------------------- -Object-document mapper ----------------------- - >>> def text_validator(node, value): - ... if value.startswith('Banana'): - ... raise BaseException, "You can't start a text with 'Banana'" - ... - - >>> def colon_validator(node, value): - ... for author in value: - ... if ',' not in author: - ... raise BaseException, "Authors's name must be in 'LastName, FirstName' format" - - >>> class Book(Document): - ... title = TextProperty(required=True, validator=text_validator) - ... authors = MultiTextProperty(required=False, validator=colon_validator) - ... pages = TextProperty() - ... - >>> class Book2(Document): - ... title = TextProperty(required=True, validator=text_validator) - ... authors = comp = CompositeTextProperty(required=False, subkeys='fl') - ... - -Instantiating a Book object:: - - >>> book1 = Book(title='Godel, Escher, Bach', - ... authors=(u'Hofstadter, Douglas',), - ... pages='777') - ... - >>> book2 = Book2(title='Godel, Escher, Bach', - ... authors=u'^lHofstadter^fDouglas') - ... - -Manipulating its attributes:: - - >>> book1.title - u'Godel, Escher, Bach' - - >>> book1.authors[0] - u'Hofstadter, Douglas' - - >>> book1.authors = (u'Hofstadter Douglas',) - Traceback (most recent call last): - ... - BaseException: Authors's name must be in 'LastName, FirstName' format - - >>> book1.authors[0] - u'Hofstadter, Douglas' - - >>> book1.authors += (u'Daiana Rose',) - Traceback (most recent call last): - ... - BaseException: Authors's name must be in 'LastName, FirstName' format - - >>> book1.authors += (u'Rose, Daiana',) - >>> book1.authors - (u'Hofstadter, Douglas', u'Rose, Daiana') - - >>> print book2.authors - [('_', u''), (u'l', u'Hofstadter'), (u'f', u'Douglas')] - - >>> book2.authors['f'] - u'Douglas' - - >>> book2.authors['j'] - Traceback (most recent call last): - ... - KeyError: 'j' - -""" from ordered import OrderedProperty, OrderedModel -from subfield import CompositeString +from isis.model.subfield import CompositeString -__all__ = ['Document', 'TextProperty', 'MultiTextProperty'] +__all__ = ['Document', 'TextProperty', 'MultiTextProperty', 'CompositeTextProperty'] class Document(OrderedModel): diff --git a/isis/model/ordered.py b/isis/model/ordered.py index 6dcbe54..59204e7 100644 --- a/isis/model/ordered.py +++ b/isis/model/ordered.py @@ -18,57 +18,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -""" -Em Python os atributos de uma classe são armazenados em um `dict`, portanto -sua ordem não é preservada. Normalmente a ordem não é realmente importante. - -Note no exemplo abaixo que a lista devolvida por `dir(l)` não preserva -a ordem em que foram declarados os atributos na classe `Livro`:: - - >>> class LivroSimples(object): - ... titulo = u'' - ... isbn = u'' - ... autores = u'' - >>> l = LivroSimples() - >>> dir(l) #doctest: +ELLIPSIS - [...'autores', 'isbn', 'titulo'...] - -Para gerar formulários automaticamente a partir da classe, é desejável -respeitar a ordem de declaração dos campos. Usando descritores e uma -metaclasse, é possível preservar esta ordem. - - >>> class Livro(OrderedModel): - ... titulo = OrderedProperty() - ... isbn = OrderedProperty() - ... autores = OrderedProperty() - >>> l2 = Livro() - >>> l2.titulo = 'O Alienista' - >>> l2.titulo - 'O Alienista' - >>> list(l2) - ['titulo', 'isbn', 'autores'] - >>> for campo in l2: print campo - titulo - isbn - autores - >>> l3 = Livro() - >>> l3.titulo - Traceback (most recent call last): - ... - AttributeError: 'Livro' object has no attribute 'titulo' - >>> l4 = Livro(titulo=u'Alice', autores=[u'Carroll', u'Tenniel'], isbn=u'9781234567890') - >>> for campo, valor in l4.iteritems(): - ... print '%-8s: %s' % (campo, valor) - titulo : Alice - isbn : 9781234567890 - autores : [u'Carroll', u'Tenniel'] - -Os descritores têm um atributo `order` que é inicializado com um contador da -classe `OrderedProperty` incrementado a cada nova instância. A metaclasse usa -este atributo `order` para ordenar uma lista com os nomes dos campos. - -""" - from operator import attrgetter class OrderedProperty(object): diff --git a/isis/model/subfield.py b/isis/model/subfield.py index a61ca77..b1f40c8 100644 --- a/isis/model/subfield.py +++ b/isis/model/subfield.py @@ -18,83 +18,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . - -""" ------------------ -Subfield parsing ------------------ - - >>> expand('John Tenniel^rillustrator') - [('_', 'John Tenniel'), ('r', 'illustrator')] - -All subfields are stripped of trailing whitespace:: - - >>> expand('John Tenniel ^rillustrator ') - [('_', 'John Tenniel'), ('r', 'illustrator')] - -Subfield keys are case-insensitive, and always converted to lower case:: - - >>> expand('John Tenniel^Rillustrator') - [('_', 'John Tenniel'), ('r', 'illustrator')] - - -Even if there is no main subfield, the '_' key is returned:: - - >>> expand('^1one^2two^3three') - [('_', ''), ('1', 'one'), ('2', 'two'), ('3', 'three')] - ---------------- -Abnormal cases ---------------- - -Empty field:: - - >>> expand('') - [('_', '')] - -Empty subfields:: - - >>> expand('aa^1^2c') # empty subfield ^1, middle position - [('_', 'aa'), ('1', ''), ('2', 'c')] - >>> expand('aa^1b^2') # empty subfield ^2, last position - [('_', 'aa'), ('1', 'b'), ('2', '')] - -Subfield keys are limited to a-z and 0-9. Invalid keys are ignored, -and the subfield delimiter is appended to the previous subfield:: - - >>> expand('John Tenniel^!illustrator') - [('_', 'John Tenniel^!illustrator')] - >>> expand('John Tenniel^rillustrator^') - [('_', 'John Tenniel'), ('r', 'illustrator^')] - >>> expand('John Tenniel^rillustrator^^') - [('_', 'John Tenniel'), ('r', 'illustrator^^')] - -To reduce the damage from duplicate delimiters in the middle of the -field, a space is added after each pair. Otherwise the example below -would seem to have an ^i subfield with the content "llustrator". -Keeping the duplicate delimiters together makes it easier to find -and fix these problems later:: - - >>> expand('John Tenniel^^illustrator') - [('_', 'John Tenniel^^ illustrator')] - ----------------------------- -Controlled subfield parsing ----------------------------- - -When a subkeys parameter is passed, only subfield markers with those keys -are expanded:: - - >>> expand('John Tenniel^rillustrator', subkeys='r') - [('_', 'John Tenniel'), ('r', 'illustrator')] - >>> expand('John Tenniel^xillustrator', subkeys='r') - [('_', 'John Tenniel^xillustrator')] - >>> expand('John Tenniel^rillustrator', subkeys='') - [('_', 'John Tenniel^rillustrator')] - - -""" - import re MAIN_SUBFIELD_KEY = '_' @@ -132,32 +55,32 @@ class CompositeString(object): ''' Represent an Isis field, with subfields, using Python native datastructures - >>> pythonic_author = CompositeString('John Tenniel^xillustrator', - ... subkeys='x') - >>> print pythonic_author - [('_', u'John Tenniel'), (u'x', u'illustrator')] + >>> author = CompositeString('John Tenniel^xillustrator', + ... subkeys='x') + >>> unicode(author) + u'John Tenniel^xillustrator' ''' - def __init__(self, isis_string, subkeys=None, encoding=DEFAULT_ENCODING): - if not isinstance(isis_string, basestring): - raise Invalid('%r value must be unicode or str instance' % isis_string) + def __init__(self, isis_raw, subkeys=None, encoding=DEFAULT_ENCODING): + if not isinstance(isis_raw, basestring): + raise Invalid('%r value must be unicode or str instance' % isis_raw) - isis_string = isis_string.decode(encoding) - self.expanded = expand(isis_string, subkeys) + self.__isis_raw = isis_raw.decode(encoding) + self.__expanded = expand(self.__isis_raw, subkeys) def __getitem__(self, key): - for subfield in self.expanded: + for subfield in self.__expanded: if subfield[0] == key: return subfield[1] else: raise KeyError(key) def __unicode__(self): - return unicode(self.expanded) + return self.__isis_raw def __str__(self): - return str(self.expanded) + return str(self.__isis_raw) def test(): diff --git a/isis/model/tests/__init__.py b/isis/model/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/isis/model/tests/test_mapper.py b/isis/model/tests/test_mapper.py new file mode 100644 index 0000000..3709c7c --- /dev/null +++ b/isis/model/tests/test_mapper.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +""" +---------------------- +Object-document mapper +---------------------- + >>> def text_validator(node, value): + ... if value.startswith('Banana'): + ... raise BaseException, "You can't start a text with 'Banana'" + ... + + >>> def colon_validator(node, value): + ... for author in value: + ... if ',' not in author: + ... raise BaseException, "Authors's name must be in 'LastName, FirstName' format" + + >>> class Book(Document): + ... title = TextProperty(required=True, validator=text_validator) + ... authors = MultiTextProperty(required=False, validator=colon_validator) + ... pages = TextProperty() + ... + >>> class Book2(Document): + ... title = TextProperty(required=True, validator=text_validator) + ... authors = comp = CompositeTextProperty(required=False, subkeys='fl') + ... + +Instantiating a Book object:: + + >>> book1 = Book(title='Godel, Escher, Bach', + ... authors=(u'Hofstadter, Douglas',), + ... pages='777') + ... + >>> book2 = Book2(title='Godel, Escher, Bach', + ... authors=u'^lHofstadter^fDouglas') + ... + +Manipulating its attributes:: + + >>> book1.title + u'Godel, Escher, Bach' + + >>> book1.authors[0] + u'Hofstadter, Douglas' + + >>> book1.authors = (u'Hofstadter Douglas',) + Traceback (most recent call last): + ... + BaseException: Authors's name must be in 'LastName, FirstName' format + + >>> book1.authors[0] + u'Hofstadter, Douglas' + + >>> book1.authors += (u'Daiana Rose',) + Traceback (most recent call last): + ... + BaseException: Authors's name must be in 'LastName, FirstName' format + + >>> book1.authors += (u'Rose, Daiana',) + >>> book1.authors + (u'Hofstadter, Douglas', u'Rose, Daiana') + + >>> print book2.authors + ^lHofstadter^fDouglas + + >>> book2.authors['f'] + u'Douglas' + + >>> book2.authors['j'] + Traceback (most recent call last): + ... + KeyError: 'j' + +""" +from isis.model.mapper import Document +from isis.model.mapper import TextProperty, MultiTextProperty, CompositeTextProperty + +def test(): + import doctest + doctest.testmod() + +if __name__=='__main__': + test() \ No newline at end of file diff --git a/isis/model/tests/test_ordered.py b/isis/model/tests/test_ordered.py new file mode 100644 index 0000000..0a1e78b --- /dev/null +++ b/isis/model/tests/test_ordered.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +""" +Em Python os atributos de uma classe são armazenados em um `dict`, portanto +sua ordem não é preservada. Normalmente a ordem não é realmente importante. + +Note no exemplo abaixo que a lista devolvida por `dir(l)` não preserva +a ordem em que foram declarados os atributos na classe `Livro`:: + + >>> class LivroSimples(object): + ... titulo = u'' + ... isbn = u'' + ... autores = u'' + >>> l = LivroSimples() + >>> dir(l) #doctest: +ELLIPSIS + [...'autores', 'isbn', 'titulo'...] + +Para gerar formulários automaticamente a partir da classe, é desejável +respeitar a ordem de declaração dos campos. Usando descritores e uma +metaclasse, é possível preservar esta ordem. + + >>> class Livro(OrderedModel): + ... titulo = OrderedProperty() + ... isbn = OrderedProperty() + ... autores = OrderedProperty() + >>> l2 = Livro() + >>> l2.titulo = 'O Alienista' + >>> l2.titulo + 'O Alienista' + >>> list(l2) + ['titulo', 'isbn', 'autores'] + >>> for campo in l2: print campo + titulo + isbn + autores + >>> l3 = Livro() + >>> l3.titulo + Traceback (most recent call last): + ... + AttributeError: 'Livro' object has no attribute 'titulo' + >>> l4 = Livro(titulo=u'Alice', autores=[u'Carroll', u'Tenniel'], isbn=u'9781234567890') + >>> for campo, valor in l4.iteritems(): + ... print '%-8s: %s' % (campo, valor) + titulo : Alice + isbn : 9781234567890 + autores : [u'Carroll', u'Tenniel'] + +Os descritores têm um atributo `order` que é inicializado com um contador da +classe `OrderedProperty` incrementado a cada nova instância. A metaclasse usa +este atributo `order` para ordenar uma lista com os nomes dos campos. + +""" +from isis.model.ordered import OrderedModel, OrderedProperty + +def test(): + import doctest + doctest.testmod() + +if __name__=='__main__': + test() \ No newline at end of file diff --git a/isis/model/tests/test_subfield.py b/isis/model/tests/test_subfield.py new file mode 100644 index 0000000..3da4bc0 --- /dev/null +++ b/isis/model/tests/test_subfield.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +""" +----------------- +Subfield parsing +----------------- + + >>> expand('John Tenniel^rillustrator') + [('_', 'John Tenniel'), ('r', 'illustrator')] + +All subfields are stripped of trailing whitespace:: + + >>> expand('John Tenniel ^rillustrator ') + [('_', 'John Tenniel'), ('r', 'illustrator')] + +Subfield keys are case-insensitive, and always converted to lower case:: + + >>> expand('John Tenniel^Rillustrator') + [('_', 'John Tenniel'), ('r', 'illustrator')] + + +Even if there is no main subfield, the '_' key is returned:: + + >>> expand('^1one^2two^3three') + [('_', ''), ('1', 'one'), ('2', 'two'), ('3', 'three')] + +--------------- +Abnormal cases +--------------- + +Empty field:: + + >>> expand('') + [('_', '')] + +Empty subfields:: + + >>> expand('aa^1^2c') # empty subfield ^1, middle position + [('_', 'aa'), ('1', ''), ('2', 'c')] + >>> expand('aa^1b^2') # empty subfield ^2, last position + [('_', 'aa'), ('1', 'b'), ('2', '')] + +Subfield keys are limited to a-z and 0-9. Invalid keys are ignored, +and the subfield delimiter is appended to the previous subfield:: + + >>> expand('John Tenniel^!illustrator') + [('_', 'John Tenniel^!illustrator')] + >>> expand('John Tenniel^rillustrator^') + [('_', 'John Tenniel'), ('r', 'illustrator^')] + >>> expand('John Tenniel^rillustrator^^') + [('_', 'John Tenniel'), ('r', 'illustrator^^')] + +To reduce the damage from duplicate delimiters in the middle of the +field, a space is added after each pair. Otherwise the example below +would seem to have an ^i subfield with the content "llustrator". +Keeping the duplicate delimiters together makes it easier to find +and fix these problems later:: + + >>> expand('John Tenniel^^illustrator') + [('_', 'John Tenniel^^ illustrator')] + +---------------------------- +Controlled subfield parsing +---------------------------- + +When a subkeys parameter is passed, only subfield markers with those keys +are expanded:: + + >>> expand('John Tenniel^rillustrator', subkeys='r') + [('_', 'John Tenniel'), ('r', 'illustrator')] + >>> expand('John Tenniel^xillustrator', subkeys='r') + [('_', 'John Tenniel^xillustrator')] + >>> expand('John Tenniel^rillustrator', subkeys='') + [('_', 'John Tenniel^rillustrator')] + + +--------------------- +CompositeString tests +--------------------- + >>> author = CompositeString('John Tenniel^xillustrator', + ... subkeys='x') + + >>> unicode(author) + u'John Tenniel^xillustrator' + + >>> str(author) + 'John Tenniel^xillustrator' +""" + +from isis.model.subfield import expand, CompositeString + +def test(): + import doctest + doctest.testmod() + +if __name__=='__main__': + test() \ No newline at end of file diff --git a/setup.py b/setup.py index e74bb0e..14249db 100644 --- a/setup.py +++ b/setup.py @@ -9,17 +9,19 @@ packages=find_packages(), long_description=README + "\n\n" + CHANGES, namespace_packages=['isis'], - #classifiers=[ - # "Development Status :: 3 - Alpha", - # "Intended Audience :: Developers", - # "Programming Language :: Python", - # "Topic :: Internet :: WWW/HTTP", - # "Topic :: Internet :: WWW/HTTP :: WSGI", - # "Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware", - # ], + classifiers=[ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Programming Language :: Python", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware", + ], keywords='isis application database bireme', author="BIREME/OPAS/OMS", - #author_email="", + author_email="isisnbp-devel@listas.bireme.br", url="http://reddes.bvsalud.org", license="LGPL v2.1 (http://www.gnu.org/licenses/lgpl-2.1.txt)", + test_suite='isis.model', + tests_require=['Nose'] )