From 42d24a5c3f1146b934e73d8e07f82dcb92a34ce5 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 24 Sep 2024 17:38:15 +0200 Subject: [PATCH] Python3 fixes: test setup OK, starting with fixes in README.txt --- loops/README.txt | 174 ++++++++++++++++---------------- loops/external/element.py | 26 +---- loops/tests/setup.py | 15 +-- loops/versioning/versionable.py | 25 +---- 4 files changed, 103 insertions(+), 137 deletions(-) diff --git a/loops/README.txt b/loops/README.txt index 0353018..3dc888c 100755 --- a/loops/README.txt +++ b/loops/README.txt @@ -48,14 +48,14 @@ top-level loops container and a concept manager: >>> cc1 = Concept() >>> concepts['cc1'] = cc1 >>> cc1.title - u'' + '' >>> loopsRoot.getLoopsUri(cc1) - u'.loops/concepts/cc1' + '.loops/concepts/cc1' - >>> cc2 = Concept(u'Zope 3') + >>> cc2 = Concept('Zope 3') >>> concepts['cc2'] = cc2 >>> cc2.title - u'Zope 3' + 'Zope 3' Now we want to relate the second concept to the first one. @@ -73,11 +73,11 @@ ConceptRelation): We can now ask our concepts for their related child and parent concepts: >>> [getName(c) for c in cc1.getChildren()] - [u'cc2'] + ['cc2'] >>> len(cc1.getParents()) 0 >>> [getName(p) for p in cc2.getParents()] - [u'cc1'] + ['cc1'] >>> len(cc2.getChildren()) 0 @@ -90,24 +90,24 @@ a special predicate 'hasType'. >>> typeObject = concepts['type'] >>> typeObject.setConceptType(typeObject) >>> typeObject.getConceptType().title - u'Type' + 'Type' - >>> concepts['unknown'] = Concept(u'Unknown Type') + >>> concepts['unknown'] = Concept('Unknown Type') >>> unknown = concepts['unknown'] >>> unknown.setConceptType(typeObject) >>> unknown.getConceptType().title - u'Type' + 'Type' >>> cc1.setConceptType(unknown) >>> cc1.getConceptType().title - u'Unknown Type' + 'Unknown Type' - >>> concepts['topic'] = Concept(u'Topic') + >>> concepts['topic'] = Concept('Topic') >>> topic = concepts['topic'] >>> topic.setConceptType(typeObject) >>> cc1.setConceptType(topic) >>> cc1.getConceptType().title - u'Topic' + 'Topic' We get a list of types using the ConceptTypeSourceList. In order for the type machinery to work we first have to provide a @@ -124,7 +124,7 @@ type manager. >>> from loops.concept import ConceptTypeSourceList >>> types = ConceptTypeSourceList(cc1) >>> sorted(t.title for t in types) - [u'Customer', u'Domain', u'Predicate', u'Topic', u'Type', u'Unknown Type'] + ['Customer', 'Domain', 'Predicate', 'Topic', 'Type', 'Unknown Type'] Using a PredicateSourceList we can retrieve a list of the available predicates. @@ -136,7 +136,7 @@ Note that the 'hasType' predicate is suppressed from this list as the corresponding relation is only assigned via the conceptType attribute: >>> sorted(t.title for t in predicates) - [u'subobject'] + ['subobject'] Concept Views ------------- @@ -146,7 +146,7 @@ Concept Views >>> children = list(view.children()) >>> [c.title for c in children] - [u'Zope 3'] + ['Zope 3'] The token attribute provided with the items returned by the children() and parents() methods identifies identifies not only the item itself but @@ -154,19 +154,19 @@ also the relationship to the context object using a combination of URIs to item and the predicate of the relationship: >>> [c.token for c in children] - [u'.loops/concepts/cc2:.loops/concepts/standard'] + ['.loops/concepts/cc2:.loops/concepts/standard'] There is also a concept configuration view that allows updating the underlying context object: - >>> cc3 = Concept(u'loops for Zope 3') + >>> cc3 = Concept('loops for Zope 3') >>> concepts['cc3'] = cc3 >>> view = ConceptConfigureView(cc1, ... TestRequest(action='assign', tokens=['.loops/concepts/cc3'])) >>> view.update() True >>> sorted(c.title for c in cc1.getChildren()) - [u'Zope 3', u'loops for Zope 3'] + ['Zope 3', 'loops for Zope 3'] >>> input = {'action': 'remove', 'qualifier': 'children', ... 'form.button.submit': 'Remove Chiildren', @@ -175,18 +175,18 @@ underlying context object: >>> view.update() True >>> sorted(c.title for c in cc1.getChildren()) - [u'loops for Zope 3'] + ['loops for Zope 3'] We can also create a new concept and assign it. >>> params = {'action': 'create', 'create.name': 'cc4', - ... 'create.title': u'New concept', + ... 'create.title': 'New concept', ... 'create.type': '.loops/concepts/topic'} >>> view = ConceptConfigureView(cc1, TestRequest(**params)) >>> view.update() True >>> sorted(c.title for c in cc1.getChildren()) - [u'New concept', u'loops for Zope 3'] + ['New concept', 'loops for Zope 3'] The concept configuration view provides methods for displaying concept types and predicates. @@ -198,15 +198,15 @@ types and predicates. >>> component.provideAdapter(LoopsTerms, (IIterableSource, IBrowserRequest), ITerms) >>> sorted((t.title, t.token) for t in view.conceptTypes()) - [(u'Customer', '.loops/concepts/customer'), - (u'Domain', '.loops/concepts/domain'), - (u'Predicate', '.loops/concepts/predicate'), - (u'Topic', '.loops/concepts/topic'), - (u'Type', '.loops/concepts/type'), - (u'Unknown Type', '.loops/concepts/unknown')] + [('Customer', '.loops/concepts/customer'), + ('Domain', '.loops/concepts/domain'), + ('Predicate', '.loops/concepts/predicate'), + ('Topic', '.loops/concepts/topic'), + ('Type', '.loops/concepts/type'), + ('Unknown Type', '.loops/concepts/unknown')] >>> sorted((t.title, t.token) for t in view.predicates()) - [(u'subobject', '.loops/concepts/standard')] + [('subobject', '.loops/concepts/standard')] Index attributes adapter ------------------------ @@ -214,10 +214,10 @@ Index attributes adapter >>> from loops.concept import IndexAttributes >>> idx = IndexAttributes(cc2) >>> idx.text() - u'cc2 Zope 3' + 'cc2 Zope 3' >>> idx.title() - u'cc2 Zope 3' + 'cc2 Zope 3' Resources and what they have to do with Concepts @@ -233,20 +233,20 @@ A common type of resource is a document: >>> from loops.interfaces import IDocument >>> from loops.resource import Document - >>> doc1 = Document(u'Zope Info') + >>> doc1 = Document('Zope Info') >>> resources['doc1'] = doc1 >>> doc1.title - u'Zope Info' + 'Zope Info' >>> doc1.data - u'' + '' >>> doc1.contentType - u'' + '' We can also directly use Resource objects; these behave like files. In fact, by using resource types we can explicitly assign a resource the 'file' type, but we will use this feature later: - >>> img = Resource(u'A png Image') + >>> img = Resource('A png Image') For testing we use some simple files from the tests directory: @@ -261,7 +261,7 @@ For testing we use some simple files from the tests directory: >>> img.contentType 'image/png' - >>> pdf = Resource(u'A pdf File') + >>> pdf = Resource('A pdf File') >>> pdf.data = open(os.path.join(path, 'test.pdf')).read() >>> pdf.getSize() 25862 @@ -287,7 +287,7 @@ from concepts to resources: ... 'tokens': ['.loops/resources/doc1:.loops/concepts/standard']} >>> view = ConceptConfigureView(cc1, TestRequest(form=form)) >>> [getName(r.context) for r in view.resources()] - [u'doc1'] + ['doc1'] >>> view.update() True >>> len(cc1.getResources()) @@ -316,10 +316,10 @@ Index attributes adapter >>> component.provideAdapter(FileAdapter, provides=IFile) >>> idx = IndexAttributes(doc1) >>> idx.text() - u'' + '' >>> idx.title() - u'doc1 Zope Info' + 'doc1 Zope Info' Views/Nodes: Menus, Menu Items, Listings, Pages, etc @@ -343,14 +343,14 @@ The view manager has already been created during setup. The view space is typically built up with nodes; a node may be a top-level menu that may contain other nodes as menu or content items: - >>> m1 = views['m1'] = Node(u'Menu') - >>> m11 = m1['m11'] = Node(u'Zope') - >>> m111 = m11['m111'] = Node(u'Zope in General') - >>> m112 = m11['m112'] = Node(u'Zope 3') + >>> m1 = views['m1'] = Node('Menu') + >>> m11 = m1['m11'] = Node('Zope') + >>> m111 = m11['m111'] = Node('Zope in General') + >>> m112 = m11['m112'] = Node('Zope 3') >>> m112.title - u'Zope 3' + 'Zope 3' >>> m112.description - u'' + '' There are a few convienence methods for accessing parent and child nodes: @@ -359,7 +359,7 @@ There are a few convienence methods for accessing parent and child nodes: >>> m11.getParentNode() is m1 True >>> [getName(child) for child in m11.getChildNodes()] - [u'm111', u'm112'] + ['m111', 'm112'] What is returned by these may be controlled by the nodeType attribute: @@ -493,14 +493,14 @@ view; these views we have to provide as multi-adapters: >>> len(tt) 9 >>> sorted((t.token, t.title) for t in view.targetTypes())[1] - ('.loops/concepts/domain', u'Domain') + ('.loops/concepts/domain', 'Domain') >>> view.update() True >>> sorted(resources.keys()) - [u'd001.txt', u'd002.txt', u'd003.txt', u'doc1', u'm1.m11.m111'] + ['d001.txt', 'd002.txt', 'd003.txt', 'doc1', 'm1.m11.m111'] >>> view.target.title, view.target.token - ('New Resource', u'.loops/resources/m1.m11.m111') + ('New Resource', '.loops/resources/m1.m11.m111') A node object provides the targetSchema of its target: @@ -537,28 +537,28 @@ view for rendering.) >>> component.provideAdapter(LoopsType) >>> view = NodeView(m112, TestRequest()) >>> view.renderTarget() - u'
'
-  >>> doc1.data = u'Test data\n\nAnother paragraph'
+  '
'
+  >>> doc1.data = 'Test data\n\nAnother paragraph'
   >>> view.renderTarget()
-  u'
Test data\n\nAnother paragraph
' + '
Test data\n\nAnother paragraph
' >>> doc1.contentType = 'text/restructured' - >>> doc1.data = u'Test data\n\nAnother `paragraph `_' + >>> doc1.data = 'Test data\n\nAnother `paragraph `_' >>> from loops.wiki.base import wikiLinksActive >>> wikiLinksActive(loopsRoot) False >>> view.renderTarget() - u'

Test data

\n

Another paragraph

\n' + '

Test data

\n

Another paragraph

\n' -u'

Test data

\n

Another Test data

\n

Another ?paragraph

\n' >>> #links = loopsRoot.getRecordManager()['links'] >>> #links['0000001'] - + If the target object is removed from its container all references to it are removed as well. (To make this work we have to handle @@ -632,7 +632,7 @@ Let's add some more nodes and reorder them: >>> m114 = Node() >>> m11['m114'] = m114 >>> m11.keys() - [u'm111', u'm112', u'm113', u'm114'] + ['m111', 'm112', 'm113', 'm114'] A special management view provides methods for moving objects down, up, to the bottom, and to the top. @@ -641,10 +641,10 @@ to the bottom, and to the top. >>> view = OrderedContainerView(m11, TestRequest()) >>> view.move_bottom(('m113',)) >>> m11.keys() - [u'm111', u'm112', u'm114', u'm113'] + ['m111', 'm112', 'm114', 'm113'] >>> view.move_up(('m114',), 1) >>> m11.keys() - [u'm111', u'm114', u'm112', u'm113'] + ['m111', 'm114', 'm112', 'm113'] Breadcrumbs @@ -661,9 +661,9 @@ Breadcrumbs >>> view = NodeView(m114, request) >>> request.annotations.setdefault('loops.view', {})['nodeView'] = view >>> view.breadcrumbs() - [{'url': 'http://127.0.0.1/loops/views/m1', 'label': u'Menu'}, - {'url': 'http://127.0.0.1/loops/views/m1/m11', 'label': u'Zope'}, - {'url': 'http://127.0.0.1/loops/views/m1/m11/m114', 'label': u''}] + [{'url': 'http://127.0.0.1/loops/views/m1', 'label': 'Menu'}, + {'url': 'http://127.0.0.1/loops/views/m1/m11', 'label': 'Zope'}, + {'url': 'http://127.0.0.1/loops/views/m1/m11/m114', 'label': ''}] End-user Forms and Special Views @@ -705,8 +705,8 @@ been created during setup. >>> custType = TypeConcept(customer) >>> custType.options [] - >>> cust1 = concepts['cust1'] = Concept(u'Zope Corporation') - >>> cust2 = concepts['cust2'] = Concept(u'cyberconcepts') + >>> cust1 = concepts['cust1'] = Concept('Zope Corporation') + >>> cust2 = concepts['cust2'] = Concept('cyberconcepts') >>> for c in (cust1, cust2): c.conceptType = customer >>> custType.options = ('qualifier:assign',) >>> ConceptType(cust1).qualifiers @@ -714,7 +714,7 @@ been created during setup. >>> form = CreateObjectForm(m112, TestRequest()) >>> form.presetTypesForAssignment - [{'token': 'loops:concept:customer', 'title': u'Customer'}] + [{'token': 'loops:concept:customer', 'title': 'Customer'}] If the node's target is a type concept we don't get any assignments because it does not make much sense to assign resources or other concepts as @@ -736,18 +736,18 @@ on data provided in this form: >>> note_tc = concepts['note'] >>> component.provideAdapter(NameChooser) - >>> request = TestRequest(form={'title': u'Test Note', - ... 'form.type': u'.loops/concepts/note', - ... 'contentType': u'text/restructured', - ... 'linkUrl': u'http://'}) + >>> request = TestRequest(form={'title': 'Test Note', + ... 'form.type': '.loops/concepts/note', + ... 'contentType': 'text/restructured', + ... 'linkUrl': 'http://'}) >>> view = NodeView(m112, request) >>> cont = CreateObject(view, request) >>> cont.update() False >>> sorted(resources.keys()) - [...u'test_note'...] + [...'test_note'...] >>> resources['test_note'].title - u'Test Note' + 'Test Note' If there is a concept selected in the combo box we assign this to the newly created object: @@ -755,8 +755,8 @@ created object: >>> from loops import util >>> topicUid = util.getUidForObject(topic) >>> predicateUid = util.getUidForObject(concepts.getDefaultPredicate()) - >>> request = TestRequest(form={'title': u'Test Note', - ... 'form.type': u'.loops/concepts/note', + >>> request = TestRequest(form={'title': 'Test Note', + ... 'form.type': '.loops/concepts/note', ... 'form.assignments.selected': ... [':'.join((topicUid, predicateUid))]}) >>> view = NodeView(m112, request) @@ -764,22 +764,22 @@ created object: >>> cont.update() False >>> sorted(resources.keys()) - [...u'test_note-2'...] + [...'test_note-2'...] >>> note = resources['test_note-2'] >>> sorted(t.__name__ for t in note.getConcepts()) - [u'note', u'topic'] + ['note', 'topic'] When creating an object its name may be automatically generated using the title of the object. Let's make sure that the name chooser also handles special and possibly critcal cases: >>> nc = NameChooser(resources) - >>> nc.chooseName(u'', Resource(u'abc: (cde)')) - u'abc__cde' - >>> nc.chooseName(u'', Resource(u'\xdcml\xe4ut')) - u'uemlaeut' - >>> nc.chooseName(u'', Resource(u'A very very loooooong title')) - u'a_title' + >>> nc.chooseName('', Resource('abc: (cde)')) + 'abc__cde' + >>> nc.chooseName('', Resource('\xdcml\xe4ut')) + 'uemlaeut' + >>> nc.chooseName('', Resource('A very very loooooong title')) + 'a_title' Editing an Object ----------------- @@ -804,22 +804,22 @@ The new technique uses the ``fields`` and ``data`` attributes... linkText textline False None >>> view.data - {'linkUrl': u'http://', 'contentType': u'text/restructured', 'data': u'', - 'linkText': u'', 'title': u'Test Note'} + {'linkUrl': 'http://', 'contentType': 'text/restructured', 'data': '', + 'linkText': '', 'title': 'Test Note'} The object is changed via a FormController adapter created for a NodeView. >>> form = dict( - ... title=u'Test Note - changed', - ... contentType=u'text/plain',) + ... title='Test Note - changed', + ... contentType='text/plain',) >>> request = TestRequest(form=form) >>> view = NodeView(m112, request) >>> cont = EditObject(view, request) >>> cont.update() False >>> resources['test_note'].title - u'Test Note - changed' + 'Test Note - changed' Virtual Targets --------------- @@ -951,7 +951,7 @@ To be done... >>> obj = resources['test_note'] >>> cxObj = cached(obj) >>> [p.object.title for p in cxObj.getAllParents()] - [u'Note', u'Type'] + ['Note', 'Type'] Security diff --git a/loops/external/element.py b/loops/external/element.py index 1e42f52..9bc0b2e 100644 --- a/loops/external/element.py +++ b/loops/external/element.py @@ -1,23 +1,6 @@ -# -# Copyright (c) 2012 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# loops.external.element -""" -Basic implementation of the elements used for the intermediate format for export +""" Basic implementation of the elements used for the intermediate format for export and import of loops objects. """ @@ -25,7 +8,7 @@ import os from zope import component from zope.cachedescriptors.property import Lazy from zope.dottedname.resolve import resolve -from zope.interface import Interface, implements +from zope.interface import Interface, implementer from zope.traversing.api import getName, traverse from cybertools.composer.interfaces import IInstance @@ -41,10 +24,9 @@ from loops.predicate import adaptedRelation from loops.view import Node +@implementer(IElement) class Element(dict): - implements(IElement) - elementType = '' posArgs = () object = None diff --git a/loops/tests/setup.py b/loops/tests/setup.py index de8a9c9..afa85d6 100644 --- a/loops/tests/setup.py +++ b/loops/tests/setup.py @@ -25,7 +25,7 @@ from zope.security.management import setSecurityPolicy from zope.securitypolicy.zopepolicy import ZopeSecurityPolicy from zope.securitypolicy.principalrole import AnnotationPrincipalRoleManager from zope.securitypolicy.rolepermission import AnnotationRolePermissionManager -from zope.session.interfaces import IClientIdManager, ISessionDataContainer +from zope.session.interfaces import IClientId, IClientIdManager, ISessionDataContainer from zope.session import session from cybertools.browser.controller import Controller @@ -37,6 +37,7 @@ from cybertools.composer.schema.field import FieldInstance, \ from cybertools.composer.schema.field import FileUploadFieldInstance from cybertools.composer.schema.grid.field import RecordsFieldInstance from cybertools.composer.schema.instance import Instance, Editor +from cybertools.meta.interfaces import IOptions from cybertools.relation.tests import IntIdsStub from cybertools.relation.registry import RelationRegistry, IIndexableRelation from cybertools.relation.interfaces import IRelation, IRelationRegistry @@ -123,7 +124,7 @@ class TestSite(object): component.provideAdapter(AnnotationPrincipalRoleManager, (ILoopsObject,)) component.provideAdapter(AnnotationRolePermissionManager, (ILoopsObject,)) component.provideUtility(principalRegistry, IAuthentication) - component.provideAdapter(session.ClientId) + component.provideAdapter(session.ClientId, provides=IClientId) component.provideAdapter(session.Session) component.provideUtility(session.RAMSessionDataContainer(), ISessionDataContainer) @@ -153,11 +154,11 @@ class TestSite(object): component.provideAdapter(BaseSecuritySetter) component.provideAdapter(ConceptSecuritySetter) component.provideAdapter(ResourceSecuritySetter) - component.provideAdapter(LoopsOptions) - component.provideAdapter(PredicateOptions) - component.provideAdapter(QueryOptions) - component.provideAdapter(TypeOptions) - component.provideUtility(GlobalOptions()) + component.provideAdapter(LoopsOptions, provides=IOptions) + component.provideAdapter(PredicateOptions, provides=IOptions) + component.provideAdapter(QueryOptions, provides=IOptions) + component.provideAdapter(TypeOptions, provides=IOptions) + component.provideUtility(GlobalOptions(), IOptions) component.provideAdapter(VersionableResource) component.provideAdapter(Instance) diff --git a/loops/versioning/versionable.py b/loops/versioning/versionable.py index f00cc08..dfe5cb4 100644 --- a/loops/versioning/versionable.py +++ b/loops/versioning/versionable.py @@ -1,29 +1,12 @@ -# -# Copyright (c) 2014 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# loops.versioning.versionable -""" -Utilities for managing version informations. +""" Utilities for managing version informations. """ from BTrees.OOBTree import OOBTree from zope import component from zope.component import adapts -from zope.interface import implements, Attribute +from zope.interface import implementer, Attribute from zope.cachedescriptors.property import Lazy from zope.schema.interfaces import IField from zope.traversing.api import getName, getParent @@ -41,11 +24,11 @@ _not_found = object() attrPattern = '__version_%s__' +@implementer(IVersionable) class VersionableResource(object): """ An adapter that enables a resource to store version information. """ - implements(IVersionable) adapts(IResource) def __init__(self, context):