# HG changeset patch # User Sylvain Thénault # Date 1285345259 -7200 # Node ID 470d8e828fda52292d69d3bf797db83ec44a093f # Parent bdc3dc94d74465c78c1841ad52d19e6b828bc1bb [test] update test to unittest2 api (still using lgc.testlib though) diff -r bdc3dc94d744 -r 470d8e828fda devtools/test/unittest_dbfill.py --- a/devtools/test/unittest_dbfill.py Fri Sep 24 18:20:57 2010 +0200 +++ b/devtools/test/unittest_dbfill.py Fri Sep 24 18:20:59 2010 +0200 @@ -68,7 +68,7 @@ def test_string(self): """test string generation""" surname = self.person_valgen.generate_attribute_value({}, 'surname', 12) - self.assertEquals(surname, u'é&surname12') + self.assertEqual(surname, u'é&surname12') def test_domain_value(self): """test value generation from a given domain value""" @@ -100,21 +100,21 @@ def test_phone(self): """tests make_tel utility""" - self.assertEquals(make_tel(22030405), '22 03 04 05') + self.assertEqual(make_tel(22030405), '22 03 04 05') def test_customized_generation(self): - self.assertEquals(self.bug_valgen.generate_attribute_value({}, 'severity', 12), + self.assertEqual(self.bug_valgen.generate_attribute_value({}, 'severity', 12), u'dangerous') - self.assertEquals(self.bug_valgen.generate_attribute_value({}, 'description', 12), + self.assertEqual(self.bug_valgen.generate_attribute_value({}, 'description', 12), u'yo') - self.assertEquals(self.person_valgen.generate_attribute_value({}, 'description', 12), + self.assertEqual(self.person_valgen.generate_attribute_value({}, 'description', 12), u'yo') class ConstraintInsertionTC(TestCase): def test_writeme(self): - self.skip('Test automatic insertion / Schema Constraints') + self.skipTest('Test automatic insertion / Schema Constraints') if __name__ == '__main__': diff -r bdc3dc94d744 -r 470d8e828fda devtools/test/unittest_httptest.py --- a/devtools/test/unittest_httptest.py Fri Sep 24 18:20:57 2010 +0200 +++ b/devtools/test/unittest_httptest.py Fri Sep 24 18:20:59 2010 +0200 @@ -15,7 +15,7 @@ def test_response_anon(self): response = self.web_get() - self.assertEquals(response.status, httplib.OK) + self.assertEqual(response.status, httplib.OK) def test_base_url(self): @@ -29,20 +29,20 @@ def test_response_denied(self): response = self.web_get() - self.assertEquals(response.status, httplib.FORBIDDEN) + self.assertEqual(response.status, httplib.FORBIDDEN) def test_login(self): response = self.web_get() if response.status != httplib.FORBIDDEN: - self.skip('Already authenticated') + self.skipTest('Already authenticated') # login self.web_login(self.admlogin, self.admpassword) response = self.web_get() - self.assertEquals(response.status, httplib.OK, response.body) + self.assertEqual(response.status, httplib.OK, response.body) # logout self.web_logout() response = self.web_get() - self.assertEquals(response.status, httplib.FORBIDDEN, response.body) + self.assertEqual(response.status, httplib.FORBIDDEN, response.body) diff -r bdc3dc94d744 -r 470d8e828fda devtools/test/unittest_qunit.py --- a/devtools/test/unittest_qunit.py Fri Sep 24 18:20:57 2010 +0200 +++ b/devtools/test/unittest_qunit.py Fri Sep 24 18:20:59 2010 +0200 @@ -20,7 +20,7 @@ def test_simple_failure(self): js_tests = list(self._test_qunit(js('test_simple_failure.js'))) - self.assertEquals(len(js_tests), 3) + self.assertEqual(len(js_tests), 3) test_1, test_2, test_3 = js_tests self.assertRaises(self.failureException, test_1[0], *test_1[1:]) self.assertRaises(self.failureException, test_2[0], *test_2[1:]) diff -r bdc3dc94d744 -r 470d8e828fda devtools/test/unittest_testlib.py --- a/devtools/test/unittest_testlib.py Fri Sep 24 18:20:57 2010 +0200 +++ b/devtools/test/unittest_testlib.py Fri Sep 24 18:20:59 2010 +0200 @@ -47,9 +47,9 @@ tests = [MyWebTest('test_error_view'), MyWebTest('test_correct_view')] result = self.runner.run(TestSuite(tests)) - self.assertEquals(result.testsRun, 2) - self.assertEquals(len(result.errors), 0) - self.assertEquals(len(result.failures), 1) + self.assertEqual(result.testsRun, 2) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 1) clean_repo_test_cls(MyWebTest) @@ -104,7 +104,7 @@ def test_source1(self): """make sure source is stored correctly""" - self.assertEquals(self.page_info.source, HTML_PAGE2) + self.assertEqual(self.page_info.source, HTML_PAGE2) def test_source2(self): """make sure source is stored correctly - raise exception""" @@ -114,47 +114,47 @@ def test_has_title_no_level(self): """tests h? tags information""" - self.assertEquals(self.page_info.has_title('Test'), True) - self.assertEquals(self.page_info.has_title('Test '), False) - self.assertEquals(self.page_info.has_title('Tes'), False) - self.assertEquals(self.page_info.has_title('Hello world !'), True) + self.assertEqual(self.page_info.has_title('Test'), True) + self.assertEqual(self.page_info.has_title('Test '), False) + self.assertEqual(self.page_info.has_title('Tes'), False) + self.assertEqual(self.page_info.has_title('Hello world !'), True) def test_has_title_level(self): """tests h? tags information""" - self.assertEquals(self.page_info.has_title('Test', level = 1), True) - self.assertEquals(self.page_info.has_title('Test', level = 2), False) - self.assertEquals(self.page_info.has_title('Test', level = 3), False) - self.assertEquals(self.page_info.has_title('Test', level = 4), False) + self.assertEqual(self.page_info.has_title('Test', level = 1), True) + self.assertEqual(self.page_info.has_title('Test', level = 2), False) + self.assertEqual(self.page_info.has_title('Test', level = 3), False) + self.assertEqual(self.page_info.has_title('Test', level = 4), False) self.assertRaises(IndexError, self.page_info.has_title, 'Test', level = 5) def test_has_title_regexp_no_level(self): """tests has_title_regexp() with no particular level specified""" - self.assertEquals(self.page_info.has_title_regexp('h[23] title'), True) + self.assertEqual(self.page_info.has_title_regexp('h[23] title'), True) def test_has_title_regexp_level(self): """tests has_title_regexp() with a particular level specified""" - self.assertEquals(self.page_info.has_title_regexp('h[23] title', 2), True) - self.assertEquals(self.page_info.has_title_regexp('h[23] title', 3), True) - self.assertEquals(self.page_info.has_title_regexp('h[23] title', 4), False) + self.assertEqual(self.page_info.has_title_regexp('h[23] title', 2), True) + self.assertEqual(self.page_info.has_title_regexp('h[23] title', 3), True) + self.assertEqual(self.page_info.has_title_regexp('h[23] title', 4), False) def test_appears(self): """tests PageInfo.appears()""" - self.assertEquals(self.page_info.appears('CW'), True) - self.assertEquals(self.page_info.appears('Logilab'), True) - self.assertEquals(self.page_info.appears('Logilab introduces'), True) - self.assertEquals(self.page_info.appears('H2 title'), False) + self.assertEqual(self.page_info.appears('CW'), True) + self.assertEqual(self.page_info.appears('Logilab'), True) + self.assertEqual(self.page_info.appears('Logilab introduces'), True) + self.assertEqual(self.page_info.appears('H2 title'), False) def test_has_link(self): """tests has_link()""" - self.assertEquals(self.page_info.has_link('Logilab'), True) - self.assertEquals(self.page_info.has_link('logilab'), False) - self.assertEquals(self.page_info.has_link('Logilab', 'http://www.logilab.org'), True) - self.assertEquals(self.page_info.has_link('Logilab', 'http://www.google.com'), False) + self.assertEqual(self.page_info.has_link('Logilab'), True) + self.assertEqual(self.page_info.has_link('logilab'), False) + self.assertEqual(self.page_info.has_link('Logilab', 'http://www.logilab.org'), True) + self.assertEqual(self.page_info.has_link('Logilab', 'http://www.google.com'), False) def test_has_link_regexp(self): """test has_link_regexp()""" - self.assertEquals(self.page_info.has_link_regexp('L[oi]gilab'), True) - self.assertEquals(self.page_info.has_link_regexp('L[ai]gilab'), False) + self.assertEqual(self.page_info.has_link_regexp('L[oi]gilab'), True) + self.assertEqual(self.page_info.has_link_regexp('L[ai]gilab'), False) if __name__ == '__main__': diff -r bdc3dc94d744 -r 470d8e828fda entities/test/unittest_base.py --- a/entities/test/unittest_base.py Fri Sep 24 18:20:57 2010 +0200 +++ b/entities/test/unittest_base.py Fri Sep 24 18:20:59 2010 +0200 @@ -44,16 +44,16 @@ self.login(u'member') entity = self.request().create_entity('Bookmark', title=u"hello", path=u'project/cubicweb') self.commit() - self.assertEquals(entity.creator.eid, self.member.eid) - self.assertEquals(entity.dc_creator(), u'member') + self.assertEqual(entity.creator.eid, self.member.eid) + self.assertEqual(entity.dc_creator(), u'member') def test_type(self): - self.assertEquals(self.member.dc_type(), 'cwuser') + self.assertEqual(self.member.dc_type(), 'cwuser') def test_entity_meta_attributes(self): # XXX move to yams - self.assertEquals(self.schema['CWUser'].meta_attributes(), {}) - self.assertEquals(dict((str(k), v) for k, v in self.schema['State'].meta_attributes().iteritems()), + self.assertEqual(self.schema['CWUser'].meta_attributes(), {}) + self.assertEqual(dict((str(k), v) for k, v in self.schema['State'].meta_attributes().iteritems()), {'description_format': ('format', 'description')}) @@ -63,20 +63,20 @@ email2 = self.execute('INSERT EmailAddress X: X address "maarten@philips.com"').get_entity(0, 0) email3 = self.execute('INSERT EmailAddress X: X address "toto@logilab.fr"').get_entity(0, 0) email1.set_relations(prefered_form=email2) - self.assertEquals(email1.prefered.eid, email2.eid) - self.assertEquals(email2.prefered.eid, email2.eid) - self.assertEquals(email3.prefered.eid, email3.eid) + self.assertEqual(email1.prefered.eid, email2.eid) + self.assertEqual(email2.prefered.eid, email2.eid) + self.assertEqual(email3.prefered.eid, email3.eid) def test_mangling(self): email = self.execute('INSERT EmailAddress X: X address "maarten.ter.huurne@philips.com"').get_entity(0, 0) - self.assertEquals(email.display_address(), 'maarten.ter.huurne@philips.com') - self.assertEquals(email.printable_value('address'), 'maarten.ter.huurne@philips.com') + self.assertEqual(email.display_address(), 'maarten.ter.huurne@philips.com') + self.assertEqual(email.printable_value('address'), 'maarten.ter.huurne@philips.com') self.vreg.config.global_set_option('mangle-emails', True) - self.assertEquals(email.display_address(), 'maarten.ter.huurne at philips dot com') - self.assertEquals(email.printable_value('address'), 'maarten.ter.huurne at philips dot com') + self.assertEqual(email.display_address(), 'maarten.ter.huurne at philips dot com') + self.assertEqual(email.printable_value('address'), 'maarten.ter.huurne at philips dot com') email = self.execute('INSERT EmailAddress X: X address "syt"').get_entity(0, 0) - self.assertEquals(email.display_address(), 'syt') - self.assertEquals(email.printable_value('address'), 'syt') + self.assertEqual(email.display_address(), 'syt') + self.assertEqual(email.printable_value('address'), 'syt') class CWUserTC(BaseEntityTC): @@ -94,19 +94,19 @@ def test_dc_title_and_name(self): e = self.execute('CWUser U WHERE U login "member"').get_entity(0, 0) - self.assertEquals(e.dc_title(), 'member') - self.assertEquals(e.name(), 'member') + self.assertEqual(e.dc_title(), 'member') + self.assertEqual(e.name(), 'member') e.set_attributes(firstname=u'bouah') - self.assertEquals(e.dc_title(), 'member') - self.assertEquals(e.name(), u'bouah') + self.assertEqual(e.dc_title(), 'member') + self.assertEqual(e.name(), u'bouah') e.set_attributes(surname=u'lôt') - self.assertEquals(e.dc_title(), 'member') - self.assertEquals(e.name(), u'bouah lôt') + self.assertEqual(e.dc_title(), 'member') + self.assertEqual(e.name(), u'bouah lôt') def test_allowed_massmail_keys(self): e = self.execute('CWUser U WHERE U login "member"').get_entity(0, 0) # Bytes/Password attributes should be omited - self.assertEquals(e.cw_adapt_to('IEmailable').allowed_massmail_keys(), + self.assertEqual(e.cw_adapt_to('IEmailable').allowed_massmail_keys(), set(('surname', 'firstname', 'login', 'last_login_time', 'creation_date', 'modification_date', 'cwuri', 'eid')) ) @@ -144,7 +144,7 @@ # no specific class for Subdivisions, the default one should be selected eclass = self.select_eclass('SubDivision') self.failUnless(eclass.__autogenerated__) - #self.assertEquals(eclass.__bases__, (AnyEntity,)) + #self.assertEqual(eclass.__bases__, (AnyEntity,)) # build class from most generic to most specific and make # sure the most specific is always selected self.vreg._loadedmods[__name__] = {} @@ -156,12 +156,12 @@ self.failUnless(eclass.__autogenerated__) self.failIf(eclass is Foo) if etype == 'SubDivision': - self.assertEquals(eclass.__bases__, (Foo,)) + self.assertEqual(eclass.__bases__, (Foo,)) else: - self.assertEquals(eclass.__bases__[0].__bases__, (Foo,)) + self.assertEqual(eclass.__bases__[0].__bases__, (Foo,)) # check Division eclass is still selected for plain Division entities eclass = self.select_eclass('Division') - self.assertEquals(eclass.__regid__, 'Division') + self.assertEqual(eclass.__regid__, 'Division') if __name__ == '__main__': unittest_main() diff -r bdc3dc94d744 -r 470d8e828fda entities/test/unittest_wfobjs.py --- a/entities/test/unittest_wfobjs.py Fri Sep 24 18:20:57 2010 +0200 +++ b/entities/test/unittest_wfobjs.py Fri Sep 24 18:20:59 2010 +0200 @@ -43,12 +43,12 @@ wf = add_wf(self, 'Company') foo = wf.add_state(u'foo', initial=True) bar = wf.add_state(u'bar') - self.assertEquals(wf.state_by_name('bar').eid, bar.eid) - self.assertEquals(wf.state_by_name('barrr'), None) + self.assertEqual(wf.state_by_name('bar').eid, bar.eid) + self.assertEqual(wf.state_by_name('barrr'), None) baz = wf.add_transition(u'baz', (foo,), bar, ('managers',)) - self.assertEquals(wf.transition_by_name('baz').eid, baz.eid) - self.assertEquals(len(baz.require_group), 1) - self.assertEquals(baz.require_group[0].name, 'managers') + self.assertEqual(wf.transition_by_name('baz').eid, baz.eid) + self.assertEqual(len(baz.require_group), 1) + self.assertEqual(baz.require_group[0].name, 'managers') def test_duplicated_state(self): wf = add_wf(self, 'Company') @@ -56,7 +56,7 @@ self.commit() wf.add_state(u'foo') ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'name-subject': 'workflow already have a state of that name'}) + self.assertEqual(ex.errors, {'name-subject': 'workflow already have a state of that name'}) # no pb if not in the same workflow wf2 = add_wf(self, 'Company') foo = wf2.add_state(u'foo', initial=True) @@ -66,7 +66,7 @@ self.commit() bar.set_attributes(name=u'foo') ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'name-subject': 'workflow already have a state of that name'}) + self.assertEqual(ex.errors, {'name-subject': 'workflow already have a state of that name'}) def test_duplicated_transition(self): wf = add_wf(self, 'Company') @@ -75,7 +75,7 @@ wf.add_transition(u'baz', (foo,), bar, ('managers',)) wf.add_transition(u'baz', (bar,), foo) ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'name-subject': 'workflow already have a transition of that name'}) + self.assertEqual(ex.errors, {'name-subject': 'workflow already have a transition of that name'}) # no pb if not in the same workflow wf2 = add_wf(self, 'Company') foo = wf.add_state(u'foo', initial=True) @@ -87,7 +87,7 @@ self.commit() biz.set_attributes(name=u'baz') ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'name-subject': 'workflow already have a transition of that name'}) + self.assertEqual(ex.errors, {'name-subject': 'workflow already have a transition of that name'}) class WorkflowTC(CubicWebTC): @@ -95,13 +95,13 @@ def setup_database(self): rschema = self.schema['in_state'] for rdef in rschema.rdefs.values(): - self.assertEquals(rdef.cardinality, '1*') + self.assertEqual(rdef.cardinality, '1*') self.member = self.create_user('member') def test_workflow_base(self): e = self.create_user('toto') iworkflowable = e.cw_adapt_to('IWorkflowable') - self.assertEquals(iworkflowable.state, 'activated') + self.assertEqual(iworkflowable.state, 'activated') iworkflowable.change_state('deactivated', u'deactivate 1') self.commit() iworkflowable.change_state('activated', u'activate 1') @@ -109,33 +109,33 @@ iworkflowable.change_state('deactivated', u'deactivate 2') self.commit() e.cw_clear_relation_cache('wf_info_for', 'object') - self.assertEquals([tr.comment for tr in e.reverse_wf_info_for], + self.assertEqual([tr.comment for tr in e.reverse_wf_info_for], ['deactivate 1', 'activate 1', 'deactivate 2']) - self.assertEquals(iworkflowable.latest_trinfo().comment, 'deactivate 2') + self.assertEqual(iworkflowable.latest_trinfo().comment, 'deactivate 2') def test_possible_transitions(self): user = self.execute('CWUser X').get_entity(0, 0) iworkflowable = user.cw_adapt_to('IWorkflowable') trs = list(iworkflowable.possible_transitions()) - self.assertEquals(len(trs), 1) - self.assertEquals(trs[0].name, u'deactivate') - self.assertEquals(trs[0].destination(None).name, u'deactivated') + self.assertEqual(len(trs), 1) + self.assertEqual(trs[0].name, u'deactivate') + self.assertEqual(trs[0].destination(None).name, u'deactivated') # test a std user get no possible transition cnx = self.login('member') # fetch the entity using the new session trs = list(cnx.user().cw_adapt_to('IWorkflowable').possible_transitions()) - self.assertEquals(len(trs), 0) + self.assertEqual(len(trs), 0) def _test_manager_deactivate(self, user): iworkflowable = user.cw_adapt_to('IWorkflowable') user.cw_clear_relation_cache('in_state', 'subject') - self.assertEquals(len(user.in_state), 1) - self.assertEquals(iworkflowable.state, 'deactivated') + self.assertEqual(len(user.in_state), 1) + self.assertEqual(iworkflowable.state, 'deactivated') trinfo = iworkflowable.latest_trinfo() - self.assertEquals(trinfo.previous_state.name, 'activated') - self.assertEquals(trinfo.new_state.name, 'deactivated') - self.assertEquals(trinfo.comment, 'deactivate user') - self.assertEquals(trinfo.comment_format, 'text/plain') + self.assertEqual(trinfo.previous_state.name, 'activated') + self.assertEqual(trinfo.new_state.name, 'deactivated') + self.assertEqual(trinfo.comment, 'deactivate user') + self.assertEqual(trinfo.comment_format, 'text/plain') return trinfo def test_change_state(self): @@ -143,7 +143,7 @@ iworkflowable = user.cw_adapt_to('IWorkflowable') iworkflowable.change_state('deactivated', comment=u'deactivate user') trinfo = self._test_manager_deactivate(user) - self.assertEquals(trinfo.transition, None) + self.assertEqual(trinfo.transition, None) def test_set_in_state_bad_wf(self): wf = add_wf(self, 'CWUser') @@ -153,7 +153,7 @@ ex = self.assertRaises(ValidationError, self.session.execute, 'SET X in_state S WHERE X eid %(x)s, S eid %(s)s', {'x': self.user().eid, 's': s.eid}) - self.assertEquals(ex.errors, {'in_state-subject': "state doesn't belong to entity's workflow. " + self.assertEqual(ex.errors, {'in_state-subject': "state doesn't belong to entity's workflow. " "You may want to set a custom workflow for this entity first."}) def test_fire_transition(self): @@ -161,10 +161,10 @@ iworkflowable = user.cw_adapt_to('IWorkflowable') iworkflowable.fire_transition('deactivate', comment=u'deactivate user') user.clear_all_caches() - self.assertEquals(iworkflowable.state, 'deactivated') + self.assertEqual(iworkflowable.state, 'deactivated') self._test_manager_deactivate(user) trinfo = self._test_manager_deactivate(user) - self.assertEquals(trinfo.transition.name, 'deactivate') + self.assertEqual(trinfo.transition.name, 'deactivate') def test_goback_transition(self): wf = self.session.user.cw_adapt_to('IWorkflowable').current_workflow @@ -179,7 +179,7 @@ self.commit() iworkflowable.fire_transition('wake up') self.commit() - self.assertEquals(iworkflowable.state, 'activated') + self.assertEqual(iworkflowable.state, 'activated') iworkflowable.fire_transition('deactivate') self.commit() iworkflowable.fire_transition('rest') @@ -187,7 +187,7 @@ iworkflowable.fire_transition('wake up') self.commit() user.clear_all_caches() - self.assertEquals(iworkflowable.state, 'deactivated') + self.assertEqual(iworkflowable.state, 'deactivated') # XXX test managers can change state without matching transition @@ -199,7 +199,7 @@ iworkflowable = req.entity_from_eid(self.member.eid).cw_adapt_to('IWorkflowable') ex = self.assertRaises(ValidationError, iworkflowable.fire_transition, 'deactivate') - self.assertEquals(ex.errors, {'by_transition-subject': "transition may not be fired"}) + self.assertEqual(ex.errors, {'by_transition-subject': "transition may not be fired"}) cnx.close() cnx = self.login('member') req = self.request() @@ -208,7 +208,7 @@ cnx.commit() ex = self.assertRaises(ValidationError, iworkflowable.fire_transition, 'activate') - self.assertEquals(ex.errors, {'by_transition-subject': "transition may not be fired"}) + self.assertEqual(ex.errors, {'by_transition-subject': "transition may not be fired"}) def test_fire_transition_owned_by(self): self.execute('INSERT RQLExpression X: X exprtype "ERQLExpression", ' @@ -255,34 +255,34 @@ state3 = mwf.add_state(u'state3') swftr1 = mwf.add_wftransition(u'swftr1', swf, state1, [(swfstate2, state2), (swfstate3, state3)]) - self.assertEquals(swftr1.destination(None).eid, swfstate1.eid) + self.assertEqual(swftr1.destination(None).eid, swfstate1.eid) # workflows built, begin test group = self.request().create_entity('CWGroup', name=u'grp1') self.commit() iworkflowable = group.cw_adapt_to('IWorkflowable') - self.assertEquals(iworkflowable.current_state.eid, state1.eid) - self.assertEquals(iworkflowable.current_workflow.eid, mwf.eid) - self.assertEquals(iworkflowable.main_workflow.eid, mwf.eid) - self.assertEquals(iworkflowable.subworkflow_input_transition(), None) + self.assertEqual(iworkflowable.current_state.eid, state1.eid) + self.assertEqual(iworkflowable.current_workflow.eid, mwf.eid) + self.assertEqual(iworkflowable.main_workflow.eid, mwf.eid) + self.assertEqual(iworkflowable.subworkflow_input_transition(), None) iworkflowable.fire_transition('swftr1', u'go') self.commit() group.clear_all_caches() - self.assertEquals(iworkflowable.current_state.eid, swfstate1.eid) - self.assertEquals(iworkflowable.current_workflow.eid, swf.eid) - self.assertEquals(iworkflowable.main_workflow.eid, mwf.eid) - self.assertEquals(iworkflowable.subworkflow_input_transition().eid, swftr1.eid) + self.assertEqual(iworkflowable.current_state.eid, swfstate1.eid) + self.assertEqual(iworkflowable.current_workflow.eid, swf.eid) + self.assertEqual(iworkflowable.main_workflow.eid, mwf.eid) + self.assertEqual(iworkflowable.subworkflow_input_transition().eid, swftr1.eid) iworkflowable.fire_transition('tr1', u'go') self.commit() group.clear_all_caches() - self.assertEquals(iworkflowable.current_state.eid, state2.eid) - self.assertEquals(iworkflowable.current_workflow.eid, mwf.eid) - self.assertEquals(iworkflowable.main_workflow.eid, mwf.eid) - self.assertEquals(iworkflowable.subworkflow_input_transition(), None) + self.assertEqual(iworkflowable.current_state.eid, state2.eid) + self.assertEqual(iworkflowable.current_workflow.eid, mwf.eid) + self.assertEqual(iworkflowable.main_workflow.eid, mwf.eid) + self.assertEqual(iworkflowable.subworkflow_input_transition(), None) # force back to swfstate1 is impossible since we can't any more find # subworkflow input transition ex = self.assertRaises(ValidationError, iworkflowable.change_state, swfstate1, u'gadget') - self.assertEquals(ex.errors, {'to_state-subject': "state doesn't belong to entity's workflow"}) + self.assertEqual(ex.errors, {'to_state-subject': "state doesn't belong to entity's workflow"}) self.rollback() # force back to state1 iworkflowable.change_state('state1', u'gadget') @@ -291,10 +291,10 @@ iworkflowable.fire_transition('tr2', u'chapeau') self.commit() group.clear_all_caches() - self.assertEquals(iworkflowable.current_state.eid, state3.eid) - self.assertEquals(iworkflowable.current_workflow.eid, mwf.eid) - self.assertEquals(iworkflowable.main_workflow.eid, mwf.eid) - self.assertListEquals(parse_hist(iworkflowable.workflow_history), + self.assertEqual(iworkflowable.current_state.eid, state3.eid) + self.assertEqual(iworkflowable.current_workflow.eid, mwf.eid) + self.assertEqual(iworkflowable.main_workflow.eid, mwf.eid) + self.assertListEqual(parse_hist(iworkflowable.workflow_history), [('state1', 'swfstate1', 'swftr1', 'go'), ('swfstate1', 'swfstate2', 'tr1', 'go'), ('swfstate2', 'state2', 'swftr1', 'exiting from subworkflow subworkflow'), @@ -318,7 +318,7 @@ mwf.add_wftransition(u'swftr1', swf, state1, [(swfstate2, state2), (swfstate2, state3)]) ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'subworkflow_exit-subject': u"can't have multiple exits on the same state"}) + self.assertEqual(ex.errors, {'subworkflow_exit-subject': u"can't have multiple exits on the same state"}) def test_swf_fire_in_a_row(self): # sub-workflow @@ -382,7 +382,7 @@ iworkflowable.fire_transition(trans) self.commit() group.clear_all_caches() - self.assertEquals(iworkflowable.state, nextstate) + self.assertEqual(iworkflowable.state, nextstate) class CustomWorkflowTC(CubicWebTC): @@ -400,12 +400,12 @@ {'wf': wf.eid, 'x': self.member.eid}) self.member.clear_all_caches() iworkflowable = self.member.cw_adapt_to('IWorkflowable') - self.assertEquals(iworkflowable.state, 'activated')# no change before commit + self.assertEqual(iworkflowable.state, 'activated')# no change before commit self.commit() self.member.clear_all_caches() - self.assertEquals(iworkflowable.current_workflow.eid, wf.eid) - self.assertEquals(iworkflowable.state, 'asleep') - self.assertEquals(iworkflowable.workflow_history, ()) + self.assertEqual(iworkflowable.current_workflow.eid, wf.eid) + self.assertEqual(iworkflowable.state, 'asleep') + self.assertEqual(iworkflowable.workflow_history, ()) def test_custom_wf_replace_state_keep_history(self): """member in inital state with some history, state is redirected and @@ -420,9 +420,9 @@ {'wf': wf.eid, 'x': self.member.eid}) self.commit() self.member.clear_all_caches() - self.assertEquals(iworkflowable.current_workflow.eid, wf.eid) - self.assertEquals(iworkflowable.state, 'asleep') - self.assertEquals(parse_hist(iworkflowable.workflow_history), + self.assertEqual(iworkflowable.current_workflow.eid, wf.eid) + self.assertEqual(iworkflowable.state, 'asleep') + self.assertEqual(parse_hist(iworkflowable.workflow_history), [('activated', 'deactivated', 'deactivate', None), ('deactivated', 'activated', 'activate', None), ('activated', 'asleep', None, 'workflow changed to "CWUser"')]) @@ -436,7 +436,7 @@ self.execute('SET X custom_workflow WF WHERE X eid %(x)s, WF eid %(wf)s', {'wf': wf.eid, 'x': self.member.eid}) ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'custom_workflow-subject': u'workflow has no initial state'}) + self.assertEqual(ex.errors, {'custom_workflow-subject': u'workflow has no initial state'}) def test_custom_wf_bad_etype(self): """try to set a custom workflow which doesn't apply to entity type""" @@ -445,7 +445,7 @@ self.execute('SET X custom_workflow WF WHERE X eid %(x)s, WF eid %(wf)s', {'wf': wf.eid, 'x': self.member.eid}) ex = self.assertRaises(ValidationError, self.commit) - self.assertEquals(ex.errors, {'custom_workflow-subject': u"workflow isn't a workflow for this type"}) + self.assertEqual(ex.errors, {'custom_workflow-subject': u"workflow isn't a workflow for this type"}) def test_del_custom_wf(self): """member in some state shared by the new workflow, nothing has to be @@ -461,12 +461,12 @@ self.execute('DELETE X custom_workflow WF WHERE X eid %(x)s, WF eid %(wf)s', {'wf': wf.eid, 'x': self.member.eid}) self.member.clear_all_caches() - self.assertEquals(iworkflowable.state, 'asleep')# no change before commit + self.assertEqual(iworkflowable.state, 'asleep')# no change before commit self.commit() self.member.clear_all_caches() - self.assertEquals(iworkflowable.current_workflow.name, "default user workflow") - self.assertEquals(iworkflowable.state, 'activated') - self.assertEquals(parse_hist(iworkflowable.workflow_history), + self.assertEqual(iworkflowable.current_workflow.name, "default user workflow") + self.assertEqual(iworkflowable.state, 'activated') + self.assertEqual(parse_hist(iworkflowable.workflow_history), [('activated', 'deactivated', 'deactivate', None), ('deactivated', 'asleep', None, 'workflow changed to "CWUser"'), ('asleep', 'activated', None, 'workflow changed to "default user workflow"'),]) @@ -492,24 +492,24 @@ {'wf': wf.eid, 'x': user.eid}) self.commit() user.clear_all_caches() - self.assertEquals(iworkflowable.state, 'asleep') - self.assertEquals([t.name for t in iworkflowable.possible_transitions()], + self.assertEqual(iworkflowable.state, 'asleep') + self.assertEqual([t.name for t in iworkflowable.possible_transitions()], ['rest']) iworkflowable.fire_transition('rest') self.commit() user.clear_all_caches() - self.assertEquals(iworkflowable.state, 'asleep') - self.assertEquals([t.name for t in iworkflowable.possible_transitions()], + self.assertEqual(iworkflowable.state, 'asleep') + self.assertEqual([t.name for t in iworkflowable.possible_transitions()], ['rest']) - self.assertEquals(parse_hist(iworkflowable.workflow_history), + self.assertEqual(parse_hist(iworkflowable.workflow_history), [('asleep', 'asleep', 'rest', None)]) user.set_attributes(surname=u'toto') # fulfill condition self.commit() iworkflowable.fire_transition('rest') self.commit() user.clear_all_caches() - self.assertEquals(iworkflowable.state, 'dead') - self.assertEquals(parse_hist(iworkflowable.workflow_history), + self.assertEqual(iworkflowable.state, 'dead') + self.assertEqual(parse_hist(iworkflowable.workflow_history), [('asleep', 'asleep', 'rest', None), ('asleep', 'asleep', 'rest', None), ('asleep', 'dead', 'sick', None),]) @@ -521,7 +521,7 @@ {'wf': wf.eid, 'x': user.eid}) self.commit() iworkflowable = user.cw_adapt_to('IWorkflowable') - self.assertEquals(iworkflowable.state, 'dead') + self.assertEqual(iworkflowable.state, 'dead') def test_auto_transition_initial_state_fired(self): wf = self.execute('Any WF WHERE ET default_workflow WF, ' @@ -534,7 +534,7 @@ user = self.create_user('member', surname=u'toto') self.commit() iworkflowable = user.cw_adapt_to('IWorkflowable') - self.assertEquals(iworkflowable.state, 'dead') + self.assertEqual(iworkflowable.state, 'dead') class WorkflowHooksTC(CubicWebTC): @@ -555,7 +555,7 @@ self.commit() initialstate = self.execute('Any N WHERE S name N, X in_state S, X eid %(x)s', {'x' : ueid})[0][0] - self.assertEquals(initialstate, u'activated') + self.assertEqual(initialstate, u'activated') # give access to users group on the user's wf transitions # so we can test wf enforcing on euser (managers don't have anymore this # enforcement @@ -592,7 +592,7 @@ iworkflowable = user.cw_adapt_to('IWorkflowable') ex = self.assertRaises(ValidationError, iworkflowable.fire_transition, 'activate') - self.assertEquals(self._cleanup_msg(ex.errors['by_transition-subject']), + self.assertEqual(self._cleanup_msg(ex.errors['by_transition-subject']), u"transition isn't allowed from") cnx.close() @@ -602,7 +602,7 @@ iworkflowable = user.cw_adapt_to('IWorkflowable') ex = self.assertRaises(ValidationError, iworkflowable.fire_transition, 'dummy') - self.assertEquals(self._cleanup_msg(ex.errors['by_transition-subject']), + self.assertEqual(self._cleanup_msg(ex.errors['by_transition-subject']), u"transition isn't allowed from") cnx.close() @@ -616,7 +616,7 @@ session.set_pool() ex = self.assertRaises(ValidationError, iworkflowable.fire_transition, 'deactivate') - self.assertEquals(self._cleanup_msg(ex.errors['by_transition-subject']), + self.assertEqual(self._cleanup_msg(ex.errors['by_transition-subject']), u"transition isn't allowed from") # get back now iworkflowable.fire_transition('activate') diff -r bdc3dc94d744 -r 470d8e828fda ext/test/unittest_rest.py --- a/ext/test/unittest_rest.py Fri Sep 24 18:20:57 2010 +0200 +++ b/ext/test/unittest_rest.py Fri Sep 24 18:20:59 2010 +0200 @@ -29,9 +29,9 @@ def test_eid_role(self): context = self.context() - self.assertEquals(rest_publish(context, ':eid:`%s`' % context.eid), + self.assertEqual(rest_publish(context, ':eid:`%s`' % context.eid), '

#%s

\n' % context.eid) - self.assertEquals(rest_publish(context, ':eid:`%s:some text`' % context.eid), + self.assertEqual(rest_publish(context, ':eid:`%s:some text`' % context.eid), '

some text

\n') def test_bad_rest_no_crash(self): diff -r bdc3dc94d744 -r 470d8e828fda goa/__init__.py --- a/goa/__init__.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,159 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""cubicweb on google appengine - -""" -__docformat__ = "restructuredtext en" - - -try: - # WARNING: do not import the google's db module here since it will take - # precedence over our own db submodule - from google.appengine.api.datastore import Key, Get, Query - from google.appengine.api.datastore_errors import BadKeyError -except ImportError: - # not in google app environment - pass -else: - - import os - _SS = os.environ.get('SERVER_SOFTWARE') - if _SS is None: - MODE = 'test' - elif _SS.startswith('Dev'): - MODE = 'dev' - else: - MODE = 'prod' - - from cubicweb.server import SOURCE_TYPES - from cubicweb.goa.gaesource import GAESource - SOURCE_TYPES['gae'] = GAESource - - - def do_monkey_patch(): - - # monkey patch yams Bytes validator since it should take a bytes string with gae - # and not a StringIO - def check_bytes(eschema, value): - """check value is a bytes string""" - return isinstance(value, str) - from yams import constraints - constraints.BASE_CHECKERS['Bytes'] = check_bytes - - def rql_for_eid(eid): - return 'Any X WHERE X eid "%s"' % eid - from cubicweb import uilib - uilib.rql_for_eid = rql_for_eid - - def typed_eid(eid): - try: - return str(Key(eid)) - except BadKeyError: - raise ValueError(eid) - import cubicweb - cubicweb.typed_eid = typed_eid - - # XXX monkey patch cubicweb.schema.CubicWebSchema to have string eid with - # optional cardinality (since eid is set after the validation) - - import re - from yams import buildobjs as ybo - - def add_entity_type(self, edef): - edef.name = edef.name.encode() - assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name) - eschema = super(CubicWebSchema, self).add_entity_type(edef) - if not eschema.final: - # automatically add the eid relation to non final entity types - rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Bytes', - cardinality='?1', uid=True) - self.add_relation_def(rdef) - rdef = ybo.RelationDefinition(eschema.type, 'identity', eschema.type) - self.add_relation_def(rdef) - self._eid_index[eschema.eid] = eschema - return eschema - - from cubicweb.schema import CubicWebSchema - CubicWebSchema.add_entity_type = add_entity_type - - - # don't reset vreg on repository set_schema - from cubicweb.server import repository - orig_set_schema = repository.Repository.set_schema - def set_schema(self, schema, resetvreg=True): - orig_set_schema(self, schema, False) - repository.Repository.set_schema = set_schema - # deactivate function ensuring relation cardinality consistency - repository.del_existing_rel_if_needed = lambda *args: None - - def get_cubes(self): - """return the list of top level cubes used by this instance""" - config = self.config - cubes = config['included-cubes'] + config['included-yams-cubes'] - return config.expand_cubes(cubes) - repository.Repository.get_cubes = get_cubes - - from rql import RQLHelper - RQLHelper.simplify = lambda x, r: None - - # activate entity caching on the server side - - def set_entity_cache(self, entity): - self.transaction_data.setdefault('_eid_cache', {})[entity.eid] = entity - - def entity_cache(self, eid): - return self.transaction_data['_eid_cache'][eid] - - def drop_entity_cache(self, eid=None): - if eid is None: - self.transaction_data['_eid_cache'] = {} - elif '_eid_cache' in self.transaction_data: - self.transaction_data['_eid_cache'].pop(eid, None) - - def datastore_get(self, key): - if isinstance(key, basestring): - key = Key(key) - try: - gentity = self.transaction_data['_key_cache'][key] - #self.critical('cached %s', gentity) - except KeyError: - gentity = Get(key) - #self.critical('Get %s', gentity) - self.transaction_data.setdefault('_key_cache', {})[key] = gentity - return gentity - - def clear_datastore_cache(self, key=None): - if key is None: - self.transaction_data['_key_cache'] = {} - else: - if isinstance(key, basestring): - key = Key(key) - self.transaction_data['_key_cache'].pop(key, None) - - from cubicweb.server.session import Session - Session.set_entity_cache = set_entity_cache - Session.entity_cache = entity_cache - Session.drop_entity_cache = drop_entity_cache - Session.datastore_get = datastore_get - Session.clear_datastore_cache = clear_datastore_cache - - from docutils.frontend import OptionParser - # avoid a call to expanduser which is not available under gae - def get_standard_config_files(self): - return self.standard_config_files - OptionParser.get_standard_config_files = get_standard_config_files diff -r bdc3dc94d744 -r 470d8e828fda goa/appobjects/__init__.py --- a/goa/appobjects/__init__.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . diff -r bdc3dc94d744 -r 470d8e828fda goa/appobjects/components.py --- a/goa/appobjects/components.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""overrides some base views for cubicweb on google appengine - -""" -__docformat__ = "restructuredtext en" - -from logilab.mtconverter import xml_escape - -from cubicweb import typed_eid -from cubicweb.selectors import one_line_rset, match_search_state, accept -from cubicweb.schema import display_name -from cubicweb.view import StartupView, EntityView -from cubicweb.web import Redirect -from cubicweb.web.views import vid_from_rset - -from google.appengine.api import mail - - -class SearchForAssociationView(EntityView): - """view called by the edition view when the user asks - to search for something to link to the edited eid - """ - id = 'search-associate' - - __select__ = one_line_rset() & match_search_state('linksearch') & accept - - def cell_call(self, row, col): - entity = self.rset.get_entity(0, 0) - role, eid, rtype, etype = self.req.search_state[1] - assert entity.eid == typed_eid(eid) - rset = entity.unrelated(rtype, etype, role, ordermethod='fetch_order') - vid = vid_from_rset(self.req, rset, self.schema) - self.w(u'
') - self.pagination(self.req, rset, w=self.w) - self.wview(vid, rset) - self.w(u'
') - - -class SchemaImageView(StartupView): - id = 'schemagraph' - binary = True - content_type = 'image/png' - def call(self): - """display global schema information""" - skipmeta = int(self.req.form.get('skipmeta', 1)) - if skipmeta: - url = self.build_url('data/schema.png') - else: - url = self.build_url('data/metaschema.png') - raise Redirect(url) - - -from cubicweb.web.views.baseviews import MetaDataView - -class GAEMetaDataView(MetaDataView): - show_eid = False - - -from cubicweb.web.views.startup import ManageView - -def entity_types_no_count(self, eschemas): - """return a list of formatted links to get a list of entities of - a each entity's types - """ - req = self.req - for eschema in eschemas: - if eschema.final or not (eschema.has_perm(req, 'read') or - eschema.has_local_role('read')): - continue - etype = eschema.type - label = display_name(req, etype, 'plural') - view = self.vreg.select('views', 'list', req, req.etype_rset(etype)) - url = view.url() - etypelink = u' %s' % (xml_escape(url), label) - yield (label, etypelink, self.add_entity_link(eschema, req)) - -ManageView.entity_types = entity_types_no_count - - -from cubicweb.web.views.basecontrollers import SendMailController - -def sendmail(self, recipient, subject, body): - sender = '%s <%s>' % ( - self.req.user.dc_title() or self.config['sender-name'], - self.req.user.cw_adapt_to('IEmailable').get_email() or self.config['sender-addr']) - mail.send_mail(sender=sender, to=recipient, - subject=subject, body=body) - -SendMailController.sendmail = sendmail diff -r bdc3dc94d744 -r 470d8e828fda goa/appobjects/dbmgmt.py --- a/goa/appobjects/dbmgmt.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,200 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""special management views to manage repository content (initialization and -restoration). - -""" -__docformat__ = "restructuredtext en" - -from os.path import exists, join, abspath -from pickle import loads, dumps - -from logilab.common.decorators import cached -from logilab.mtconverter import xml_escape - -from cubicweb.selectors import none_rset, match_user_groups -from cubicweb.view import StartupView -from cubicweb.web import Redirect -from cubicweb.goa.dbinit import fix_entities, init_persistent_schema, insert_versions - -from google.appengine.api.datastore import Entity, Key, Get, Put, Delete -from google.appengine.api.datastore_types import Blob -from google.appengine.api.datastore_errors import EntityNotFoundError - - -def _get_status(name, create=True): - key = Key.from_path('EApplicationStatus', name) - try: - status = Get(key) - except EntityNotFoundError: - if create: - status = Entity('EApplicationStatus', name=name) - else: - status = None - return status - - -class AuthInfo(StartupView): - """special management view to get cookie values to give to laxctl commands - which are doing datastore administration requests - """ - id = 'authinfo' - __select__ = none_rset() & match_user_groups('managers') - - def call(self): - cookie = self.req.get_cookie() - values = [] - if self.config['use-google-auth']: - for param in ('ACSID', 'dev_appserver_login'): - morsel = cookie.get(param) - if morsel: - values.append('%s=%s' % (param, morsel.value)) - break - values.append('__session=%s' % cookie['__session'].value) - self.w(u"

pass this flag to the client: --cookie='%s'

" - % xml_escape('; '.join(values))) - - - -class ContentInit(StartupView): - """special management view to initialize content of a repository, - step by step to avoid depassing quotas - """ - id = 'contentinit' - __select__ = none_rset() & match_user_groups('managers') - - def server_session(self): - ssession = self.config.repo_session(self.req.cnx.sessionid) - ssession.set_pool() - return ssession - - def end_core_step(self, msg, status, stepid): - status['cpath'] = '' - status['stepid'] = stepid - Put(status) - self.msg(msg) - - def call(self): - status = _get_status('creation') - if status.get('finished'): - self.redirect('process already completed') - config = self.config - # execute cubicweb's post script - #mhandler.exec_event_script('post%s' % event) - # execute cubes'post script if any - paths = [p for p in config.cubes_path() + [config.apphome] - if exists(join(p, 'migration'))] - paths = [abspath(p) for p in (reversed(paths))] - cpath = status.get('cpath') - if cpath is None and status.get('stepid') is None: - init_persistent_schema(self.server_session(), self.schema) - self.end_core_step(u'inserted schema entities', status, 0) - return - if cpath == '' and status.get('stepid') == 0: - fix_entities(self.schema) - self.end_core_step(u'fixed bootstrap groups and users', status, 1) - return - if cpath == '' and status.get('stepid') == 1: - insert_versions(self.server_session(), self.config) - self.end_core_step(u'inserted software versions', status, None) - return - for i, path in enumerate(paths): - if not cpath or cpath == path: - self.info('running %s', path) - stepid = status.get('stepid') - context = status.get('context') - if context is not None: - context = loads(context) - else: - context = {} - stepid = self._migrhandler.exec_event_script( - 'postcreate', path, 'stepable_postcreate', stepid, context) - if stepid is None: # finished for this script - # reset script state - context = stepid = None - # next time, go to the next script - self.msg(u'finished postcreate for %s' % path) - try: - path = paths[i+1] - self.continue_link() - except IndexError: - status['finished'] = True - path = None - self.redirect('process completed') - else: - if context.get('stepidx'): - self.msg(u'created %s entities for step %s of %s' % ( - context['stepidx'], stepid, path)) - else: - self.msg(u'finished postcreate step %s for %s' % ( - stepid, path)) - context = Blob(dumps(context)) - self.continue_link() - status['context'] = context - status['stepid'] = stepid - status['cpath'] = path - break - else: - if not cpath: - # nothing to be done - status['finished'] = True - self.redirect('process completed') - else: - # Note the error: is expected by the laxctl command line tool, - # deal with this if internationalization is introduced - self.msg(u'error: strange creation state, can\'t find %s' - % cpath) - self.w(u'
click here to ' - 'delete all datastore content so process can be ' - 'reinitialized
' % xml_escape(self.req.base_url())) - Put(status) - - @property - @cached - def _migrhandler(self): - return self.config.migration_handler(self.schema, interactive=False, - cnx=self.req.cnx, - repo=self.config.repository()) - - def msg(self, msg): - self.w(u'
%s
' % xml_escape(msg)) - def redirect(self, msg): - raise Redirect(self.req.build_url('', msg)) - def continue_link(self): - self.w(u'continue
' % xml_escape(self.req.url())) - - -class ContentClear(StartupView): - id = 'contentclear' - __select__ = none_rset() & match_user_groups('managers') - skip_etypes = ('CWGroup', 'CWUser') - - def call(self): - # XXX should use unsafe execute with all hooks deactivated - # XXX step by catching datastore errors? - for eschema in self.schema.entities(): - if eschema.final or eschema in self.skip_etypes: - continue - self.req.execute('DELETE %s X' % eschema) - self.w(u'deleted all %s entities
' % eschema) - status = _get_status('creation', create=False) - if status: - Delete(status) - self.w(u'done
') - self.w(u'click here to start the data ' - 'initialization process
' % self.req.base_url()) diff -r bdc3dc94d744 -r 470d8e828fda goa/appobjects/gauthservice.py --- a/goa/appobjects/gauthservice.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""authentication using google authentication service - -""" -__docformat__ = "restructuredtext en" - -from cubicweb.web.views.basecomponents import UserLink -from cubicweb.web.views.actions import LogoutAction - -from google.appengine.api import users - - -class GACWUserLink(UserLink): - - def anon_user_link(self): - self.w(self.req._('anonymous')) - self.w(u' [%s]' - % (users.create_login_url(self.req.url()), self.req._('login'))) - -class GAELogoutAction(LogoutAction): - - def url(self): - return users.create_logout_url(self.req.build_url('logout') ) - -def registration_callback(vreg): - if hasattr(vreg.config, 'has_resource'): - vreg.register(GACWUserLink, clear=True) - vreg.register(GAELogoutAction, clear=True) diff -r bdc3dc94d744 -r 470d8e828fda goa/appobjects/sessions.py --- a/goa/appobjects/sessions.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,291 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""persistent sessions stored in big table - - -XXX TODO: -* cleanup persistent session -* use user as ancestor? -""" -__docformat__ = "restructuredtext en" - -from pickle import loads, dumps -from time import localtime, strftime - -from logilab.common.decorators import cached, clear_cache - -from cubicweb import BadConnectionId -from cubicweb.dbapi import Connection, ConnectionProperties, repo_connect -from cubicweb.selectors import none_rset, match_user_groups -from cubicweb.server.session import Session -from cubicweb.web import InvalidSession -from cubicweb.web.application import AbstractSessionManager -from cubicweb.web.application import AbstractAuthenticationManager - -from google.appengine.api.datastore import Key, Entity, Get, Put, Delete, Query -from google.appengine.api.datastore_errors import EntityNotFoundError -from google.appengine.api.datastore_types import Blob - -try: - del Connection.__del__ -except AttributeError: - pass # already deleted - - -class GAEAuthenticationManager(AbstractAuthenticationManager): - """authenticate user associated to a request and check session validity, - using google authentication service - """ - - def __init__(self, *args, **kwargs): - super(GAEAuthenticationManager, self).__init__(*args, **kwargs) - self._repo = self.config.repository(vreg=self.vreg) - - def authenticate(self, req, _login=None, _password=None): - """authenticate user and return an established connection for this user - - :raise ExplicitLogin: if authentication is required (no authentication - info found or wrong user/password) - """ - if _login is not None: - login, password = _login, _password - else: - login, password = req.get_authorization() - # remove possibly cached cursor coming from closed connection - clear_cache(req, 'cursor') - cnxprops = ConnectionProperties(self.vreg.config.repo_method, - close=False, log=False) - cnx = repo_connect(self._repo, login, password=password, cnxprops=cnxprops) - self._init_cnx(cnx, login, password) - # associate the connection to the current request - req.set_connection(cnx) - return cnx - - def _init_cnx(self, cnx, login, password): - cnx.anonymous_connection = self.config.is_anonymous_user(login) - cnx.vreg = self.vreg - cnx.login = login - cnx.password = password - - -class GAEPersistentSessionManager(AbstractSessionManager): - """manage session data associated to a session identifier""" - - def __init__(self, vreg, *args, **kwargs): - super(GAEPersistentSessionManager, self).__init__(vreg, *args, **kwargs) - self._repo = self.config.repository(vreg=vreg) - - def get_session(self, req, sessionid): - """return existing session for the given session identifier""" - # search a record for the given session - key = Key.from_path('CubicWebSession', 'key_' + sessionid, parent=None) - try: - record = Get(key) - except EntityNotFoundError: - raise InvalidSession() - repo = self._repo - if self.has_expired(record): - repo._sessions.pop(sessionid, None) - Delete(record) - raise InvalidSession() - # associate it with a repository session - try: - reposession = repo._get_session(sessionid) - user = reposession.user - # touch session to avoid closing our own session when sessions are - # cleaned (touch is done on commit/rollback on the server side, too - # late in that case) - reposession._touch() - except BadConnectionId: - # can't found session in the repository, this probably mean the - # session is not yet initialized on this server, hijack the repo - # to create it - # use an internal connection - ssession = repo.internal_session() - # try to get a user object - try: - user = repo.authenticate_user(ssession, record['login'], - record['password']) - finally: - ssession.close() - reposession = Session(user, self._repo, _id=sessionid) - self._repo._sessions[sessionid] = reposession - cnx = Connection(self._repo, sessionid) - return self._get_proxy(req, record, cnx, user) - - def open_session(self, req): - """open and return a new session for the given request""" - cnx = self.authmanager.authenticate(req) - # avoid rebuilding a user - user = self._repo._get_session(cnx.sessionid).user - # build persistent record for session data - record = Entity('CubicWebSession', name='key_' + cnx.sessionid) - record['login'] = cnx.login - record['password'] = cnx.password - record['anonymous_connection'] = cnx.anonymous_connection - Put(record) - return self._get_proxy(req, record, cnx, user) - - def close_session(self, proxy): - """close session on logout or on invalid session detected (expired out, - corrupted...) - """ - proxy.close() - - def current_sessions(self): - for record in Query('CubicWebSession').Run(): - yield ConnectionProxy(record) - - def _get_proxy(self, req, record, cnx, user): - proxy = ConnectionProxy(record, cnx, user) - user.req = req - req.set_connection(proxy, user) - return proxy - - -class ConnectionProxy(object): - - def __init__(self, record, cnx=None, user=None): - self.__record = record - self.__cnx = cnx - self.__user = user - self.__data = None - self.__is_dirty = False - self.sessionid = record.key().name()[4:] # remove 'key_' prefix - - def __repr__(self): - sstr = '') - self.w(u'%s web sessions closed
\n' % nbclosed) - # clean repository sessions - repo = self.config.repository(vreg=self.vreg) - nbclosed = repo.clean_sessions() - self.w(u'%s repository sessions closed
\n' % nbclosed) - self.w(u'%s remaining sessions
\n' % remaining) - self.w(u'') - - -def registration_callback(vreg): - vreg.register(SessionsCleaner) - vreg.register(GAEAuthenticationManager, clear=True) - vreg.register(GAEPersistentSessionManager, clear=True) diff -r bdc3dc94d744 -r 470d8e828fda goa/bin/laxctl --- a/goa/bin/laxctl Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#!/usr/bin/env python - -import sys -import os.path as osp - -APPLROOT = osp.abspath(osp.join(osp.dirname(osp.abspath(__file__)), '..')) -if APPLROOT not in sys.path: - sys.path.insert(0, APPLROOT) -CUBES_DIR = osp.join(APPLROOT, 'cw-cubes') -if CUBES_DIR not in sys.path: - sys.path.insert(1, CUBES_DIR) - -try: - import custom -except ImportError, exc: - print exc - sys.exit(2) - -from tools.laxctl import run -run() diff -r bdc3dc94d744 -r 470d8e828fda goa/db.py --- a/goa/db.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,469 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""provide replacement classes for gae db module, so that a gae model can be -used as base for a cubicweb application by simply replacing :: - - from google.appengine.ext import db - -by - - from cubicweb.goa import db - -The db.model api should be fully featured by replacement classes, with the -following differences: - -* all methods returning `google.appengine.ext.db.Model` instance(s) will return - `cubicweb.goa.db.Model` instance instead (though you should see almost no - difference since those instances have the same api) - -* class methods returning model instance take a `req` as first argument, unless - they are called through an instance, representing the current request - (accessible through `self.req` on almost all objects) - -* XXX no instance._set attributes, use instance.reverse_ - instead -* XXX reference property always return a list of objects, not the instance -* XXX name/collection_name argument of properties constructor are ignored -* XXX ListProperty - -""" -__docformat__ = "restructuredtext en" - -from copy import deepcopy - -from logilab.common.decorators import cached, iclassmethod - -from cubicweb import Binary, entities -from cubicweb.req import RequestSessionBase -from cubicweb.rset import ResultSet -from cubicweb.entity import metaentity -from cubicweb.server.utils import crypt_password -from cubicweb.goa import MODE -from cubicweb.goa.dbinit import init_relations - -from google.appengine.api.datastore import Get, Put, Key, Entity, Query -from google.appengine.api.datastore import NormalizeAndTypeCheck, RunInTransaction -from google.appengine.api.datastore_types import Text, Blob -from google.appengine.api.datastore_errors import BadKeyError - -# XXX remove this dependancy -from google.appengine.ext import db - - -def rset_from_objs(req, objs, attrs=('eid',), rql=None, args=None): - """return a ResultSet instance for list of objects""" - if objs is None: - objs = () - elif isinstance(objs, Entity): - objs = (objs,) - if rql is None: - rql = 'Any X' - rows = [] - description = [] - rset = ResultSet(rows, rql, args, description=description) - vreg = req.vreg - for i, obj in enumerate(objs): - line = [] - linedescr = [] - eschema = vreg.schema.eschema(obj.kind()) - for j, attr in enumerate(attrs): - if attr == 'eid': - value = obj.key() - obj.row, obj.col = i, j - descr = eschema.type - value = str(value) - else: - value = obj[attr] - descr = str(eschema.destination(attr)) - line.append(value) - linedescr.append(descr) - rows.append(line) - description.append(linedescr) - for j, attr in enumerate(attrs): - if attr == 'eid': - entity = vreg.etype_class(eschema.type)(req, rset, i, j) - rset._get_entity_cache_ = {(i, j): entity} - rset.rowcount = len(rows) - rset.req = req - return rset - - -def needrequest(wrapped): - def wrapper(cls, *args, **kwargs): - req = kwargs.pop('req', None) - if req is None and args and isinstance(args[0], RequestSessionBase): - args = list(args) - req = args.pop(0) - if req is None: - req = getattr(cls, 'req', None) - if req is None: - raise Exception('either call this method on an instance or ' - 'specify the req argument') - return wrapped(cls, req, *args, **kwargs) - return iclassmethod(wrapper) - - -class gaedbmetaentity(metaentity): - """metaclass for goa.db.Model classes: filter entity / db model part, - put aside the db model part for later creation of db model class. - """ - def __new__(mcs, name, bases, classdict): - if not 'id' in classdict: - classdict['id'] = name - entitycls = super(gaedbmetaentity, mcs).__new__(mcs, name, bases, classdict) - return entitycls - - -TEST_MODELS = {} - -def extract_dbmodel(entitycls): - if MODE == 'test' and entitycls in TEST_MODELS: - dbclassdict = TEST_MODELS[entitycls] - else: - dbclassdict = {} - for attr, value in entitycls.__dict__.items(): - if isinstance(value, db.Property) or isinstance(value, ReferencePropertyStub): - dbclassdict[attr] = value - # don't remove attr from entitycls, this make tests fail, and it's anyway - # overwritten by descriptor at class initialization time - #delattr(entitycls, attr) - if MODE == 'test': - TEST_MODELS[entitycls] = dbclassdict - dbclassdict = deepcopy(dbclassdict) - for propname, prop in TEST_MODELS[entitycls].iteritems(): - if getattr(prop, 'reference_class', None) is db._SELF_REFERENCE: - dbclassdict[propname].reference_class = db._SELF_REFERENCE - return dbclassdict - - -class Model(entities.AnyEntity): - id = 'Any' - __metaclass__ = gaedbmetaentity - - row = col = 0 - - @classmethod - def __initialize__(cls): - super(Model, cls).__initialize__() - cls._attributes = frozenset(rschema for rschema in cls.e_schema.subject_relations() - if rschema.final) - - def __init__(self, *args, **kwargs): - # db.Model prototype: - # __init__(self, parent=None, key_name=None, **kw) - # - # Entity prototype: - # __init__(self, req, rset, row=None, col=0) - if args and isinstance(args[0], RequestSessionBase) or 'req' in kwargs: - super(Model, self).__init__(*args, **kwargs) - self._gaeinitargs = None - else: - super(Model, self).__init__(None, None) - # if Model instances are given in kwargs, turn them into db model - for key, val in kwargs.iteritems(): - if key in self.e_schema.subject_relations() and not self.e_schema.schema[key].final: - if isinstance(kwargs, (list, tuple)): - val = [isinstance(x, Model) and x._dbmodel or x for x in val] - elif isinstance(val, Model): - val = val._dbmodel - kwargs[key] = val.key() - self._gaeinitargs = (args, kwargs) - - def __repr__(self): - return '' % ( - self.e_schema, self.eid, self.keys(), id(self)) - - def _cubicweb_to_datastore(self, attr, value): - attr = attr[2:] # remove 's_' / 'o_' prefix - if attr in self._attributes: - tschema = self.e_schema.destination(attr) - if tschema == 'String': - if len(value) > 500: - value = Text(value) - elif tschema == 'Password': - # if value is a Binary instance, this mean we got it - # from a query result and so it is already encrypted - if isinstance(value, Binary): - value = value.getvalue() - else: - value = crypt_password(value) - elif tschema == 'Bytes': - if isinstance(value, Binary): - value = value.getvalue() - value = Blob(value) - else: - value = Key(value) - return value - - def _to_gae_dict(self, convert=True): - gaedict = {} - for attr, value in self.iteritems(): - attr = 's_' + attr - if value is not None and convert: - value = self._cubicweb_to_datastore(attr, value) - gaedict[attr] = value - return gaedict - - def to_gae_model(self): - dbmodel = self._dbmodel - dbmodel.update(self._to_gae_dict()) - return dbmodel - - @property - @cached - def _dbmodel(self): - if self.has_eid(): - assert self._gaeinitargs is None - try: - return self.req.datastore_get(self.eid) - except AttributeError: # self.req is not a server session - return Get(self.eid) - self._cw_set_defaults() - values = self._to_gae_dict(convert=False) - parent = key_name = _app = None - if self._gaeinitargs is not None: - args, kwargs = self._gaeinitargs - args = list(args) - if args: - parent = args.pop(0) - if args: - key_name = args.pop(0) - if args: - _app = args.pop(0) - assert not args - if 'parent' in kwargs: - assert parent is None - parent = kwargs.pop('parent') - if 'key_name' in kwargs: - assert key_name is None - key_name = kwargs.pop('key_name') - if '_app' in kwargs: - assert _app is None - _app = kwargs.pop('_app') - - for key, value in kwargs.iteritems(): - if key in self._attributes: - values['s_'+key] = value - else: - kwargs = None - if key_name is None: - key_name = self.db_key_name() - if key_name is not None: - key_name = 'key_' + key_name - for key, value in values.iteritems(): - if value is None: - continue - values[key] = self._cubicweb_to_datastore(key, value) - entity = Entity(self.id, parent, _app, key_name) - entity.update(values) - init_relations(entity, self.e_schema) - return entity - - def db_key_name(self): - """override this method to control datastore key name that should be - used at entity creation. - - Note that if this function return something else than None, the returned - value will be prefixed by 'key_' to build the actual key name. - """ - return None - - def metainformation(self): - return {'type': self.id, 'source': {'uri': 'system'}, 'extid': None} - - def view(self, vid, __registry='views', **kwargs): - """shortcut to apply a view on this entity""" - return self.vreg[__registry].render(vid, self.req, rset=self.rset, - row=self.row, col=self.col, **kwargs) - - @classmethod - def _rest_attr_info(cls): - mainattr, needcheck = super(Model, cls)._rest_attr_info() - if needcheck: - return 'eid', False - return mainattr, needcheck - - def get_value(self, name): - try: - value = self[name] - except KeyError: - if not self.has_eid(): - return None - value = self._dbmodel.get('s_'+name) - if value is not None: - if isinstance(value, Text): - value = unicode(value) - elif isinstance(value, Blob): - value = Binary(str(value)) - self[name] = value - return value - - def has_eid(self): - if self.eid is None: - return False - try: - Key(self.eid) - return True - except BadKeyError: - return False - - def complete(self, skip_bytes=True): - pass - - def unrelated(self, rtype, targettype, role='subject', limit=None, - ordermethod=None): - # XXX dumb implementation - if limit is not None: - objs = Query(str(targettype)).Get(limit) - else: - objs = Query(str(targettype)).Run() - return rset_from_objs(self.req, objs, ('eid',), - 'Any X WHERE X is %s' % targettype) - - def key(self): - return Key(self.eid) - - def put(self, req=None): - if req is not None and self.req is None: - self.req = req - dbmodel = self.to_gae_model() - key = Put(dbmodel) - self.eid = str(key) - if self.req is not None and self.rset is None: - self.rset = rset_from_objs(self.req, dbmodel, ('eid',), - 'Any X WHERE X eid %(x)s', {'x': self.eid}) - self.row = self.col = 0 - return dbmodel - - @needrequest - def get(cls, req, keys): - # if check if this is a dict.key call - if isinstance(cls, Model) and keys in cls._attributes: - return super(Model, cls).get(keys) - rset = rset_from_objs(req, Get(keys), ('eid',), - 'Any X WHERE X eid IN %(x)s', {'x': keys}) - return list(rset.entities()) - - @needrequest - def get_by_id(cls, req, ids, parent=None): - if isinstance(parent, Model): - parent = parent.key() - ids, multiple = NormalizeAndTypeCheck(ids, (int, long)) - keys = [Key.from_path(cls.kind(), id, parent=parent) - for id in ids] - rset = rset_from_objs(req, Get(keys)) - return list(rset.entities()) - - @classmethod - def get_by_key_name(cls, req, key_names, parent=None): - if isinstance(parent, Model): - parent = parent.key() - key_names, multiple = NormalizeAndTypeCheck(key_names, basestring) - keys = [Key.from_path(cls.kind(), name, parent=parent) - for name in key_names] - rset = rset_from_objs(req, Get(keys)) - return list(rset.entities()) - - @classmethod - def get_or_insert(cls, req, key_name, **kwds): - def txn(): - entity = cls.get_by_key_name(key_name, parent=kwds.get('parent')) - if entity is None: - entity = cls(key_name=key_name, **kwds) - entity.put() - return entity - return RunInTransaction(txn) - - @classmethod - def all(cls, req): - rset = rset_from_objs(req, Query(cls.id).Run()) - return list(rset.entities()) - - @classmethod - def gql(cls, req, query_string, *args, **kwds): - raise NotImplementedError('use rql') - - @classmethod - def kind(cls): - return cls.id - - @classmethod - def properties(cls): - raise NotImplementedError('use eschema') - - def dynamic_properties(self): - raise NotImplementedError('use eschema') - - def cw_is_saved(self): - return self.has_eid() - - def parent(self): - parent = self._dbmodel.parent() - if not parent is None: - rset = rset_from_objs(self.req, (parent,), ('eid',), - 'Any X WHERE X eid %(x)s', {'x': parent.key()}) - parent = rset.get_entity(0, 0) - return parent - - def parent_key(self): - return self.parent().key() - - def to_xml(self): - return self._dbmodel.ToXml() - -# hijack AnyEntity class -entities.AnyEntity = Model - -BooleanProperty = db.BooleanProperty -URLProperty = db.URLProperty -DateProperty = db.DateProperty -DateTimeProperty = db.DateTimeProperty -TimeProperty = db.TimeProperty -StringProperty = db.StringProperty -TextProperty = db.TextProperty -BlobProperty = db.BlobProperty -IntegerProperty = db.IntegerProperty -FloatProperty = db.FloatProperty -ListProperty = db.ListProperty -SelfReferenceProperty = db.SelfReferenceProperty -UserProperty = db.UserProperty - - -class ReferencePropertyStub(object): - def __init__(self, cls, args, kwargs): - self.cls = cls - self.args = args - self.kwargs = kwargs - self.required = False - self.__dict__.update(kwargs) - self.creation_counter = db.Property.creation_counter - db.Property.creation_counter += 1 - - @property - def data_type(self): - class FakeDataType(object): - @staticmethod - def kind(): - return self.cls.__name__ - return FakeDataType - -def ReferenceProperty(cls, *args, **kwargs): - if issubclass(cls, db.Model): - cls = db.class_for_kind(cls.__name__) - return db.ReferenceProperty(cls, *args, **kwargs) - return ReferencePropertyStub(cls, args, kwargs) diff -r bdc3dc94d744 -r 470d8e828fda goa/dbinit.py --- a/goa/dbinit.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""some utility functions for datastore initialization. - -""" -__docformat__ = "restructuredtext en" - -from google.appengine.api.datastore import Key, Entity, Put, Get, Query -from google.appengine.api import datastore_errors - -_GROUP_CACHE = {} # XXX use memcache - -def _get_group(groupname): - try: - return _GROUP_CACHE[groupname] - except KeyError: - key = Key.from_path('CWGroup', 'key_' + groupname, parent=None) - try: - group = Get(key) - except datastore_errors.EntityNotFoundError: - raise Exception('can\'t find required group %s, is your instance ' - 'correctly initialized (eg did you run the ' - 'initialization script) ?' % groupname) - _GROUP_CACHE[groupname] = group - return group - - -def create_user(login, password, groups): - """create a cubicweb user""" - from cubicweb.server.utils import crypt_password - user = Entity('CWUser', name=login) - user['s_login'] = unicode(login) - user['s_upassword'] = crypt_password(password) - set_user_groups(user, groups) - Put(user) - return user - -def create_groups(): - """create initial cubicweb groups""" - for groupname in ('managers', 'users', 'guests'): - group = Entity('CWGroup', name='key_' + groupname) - group['s_name'] = unicode(groupname) - Put(group) - _GROUP_CACHE[groupname] = group - -def set_user_groups(user, groups): - """set user in the given groups (as string). The given user entity - (datastore.Entity) is not putted back to the repository, this is the caller - responsability. - """ - groups = [_get_group(g) for g in groups] - user['s_in_group'] = [g.key() for g in groups] or None - for group in groups: - try: - group['o_in_group'].append(user.key()) - except (KeyError, AttributeError): - group['o_in_group'] = [user.key()] - Put(group) - -def init_relations(gaeentity, eschema): - """set None for every subject relations which is not yet defined""" - for rschema in eschema.subject_relations(): - if rschema in ('identity', 'has_text'): - continue - dsrelation = 's_' + rschema.type - if not dsrelation in gaeentity: - gaeentity[dsrelation] = None - for rschema in eschema.object_relations(): - if rschema == 'identity': - continue - dsrelation = 'o_' + rschema.type - if not dsrelation in gaeentity: - gaeentity[dsrelation] = None - -def fix_entities(schema): - for etype in ('CWUser', 'CWGroup'): - eschema = schema.eschema(etype) - for gaeentity in Query(etype).Run(): - init_relations(gaeentity, eschema) - # XXX o_is on CWEType entity - gaeentity['s_is'] = Key.from_path('CWEType', 'key_' + etype, parent=None) - Put(gaeentity) - -def init_persistent_schema(ssession, schema): - execute = ssession.execute - rql = ('INSERT CWEType X: X name %(name)s, X description %(descr)s,' - 'X final FALSE') - eschema = schema.eschema('CWEType') - execute(rql, {'name': u'CWEType', 'descr': unicode(eschema.description)}) - for eschema in schema.entities(): - if eschema.final or eschema == 'CWEType': - continue - execute(rql, {'name': unicode(eschema), - 'descr': unicode(eschema.description)}) - -def insert_versions(ssession, config): - execute = ssession.execute - # insert versions - execute('INSERT CWProperty X: X pkey %(pk)s, X value%(v)s', - {'pk': u'system.version.cubicweb', - 'v': unicode(config.cubicweb_version())}) - for cube in config.cubes(): - execute('INSERT CWProperty X: X pkey %(pk)s, X value%(v)s', - {'pk': u'system.version.%s' % cube, - 'v': unicode(config.cube_version(cube))}) diff -r bdc3dc94d744 -r 470d8e828fda goa/dbmyams.py --- a/goa/dbmyams.py Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,223 +0,0 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""extends yams to be able to load google appengine's schemas - -MISSING FEATURES: - - ListProperty, StringList, EmailProperty, etc. (XXX) - - ReferenceProperty.verbose_name, collection_name, etc. (XXX) - -XXX proprify this knowing we'll use goa.db -""" - -from os.path import join -from datetime import datetime, date, time - -from google.appengine.ext import db -from google.appengine.api import datastore_types - -from yams.buildobjs import (String, Int, Float, Boolean, Date, Time, Datetime, - Bytes, SubjectRelation) -from yams.buildobjs import metadefinition, EntityType - -from cubicweb.schema import CubicWebSchemaLoader -from cubicweb.goa import db as goadb - -# db.Model -> yams ############################################################ - -DBM2Y_TYPESMAP = { - basestring: String, - datastore_types.Text: String, - int: Int, - float: Float, - bool: Boolean, - time: Time, - date: Date, - datetime: Datetime, - datastore_types.Blob: Bytes, - } - - -def dbm2y_default_factory(prop, **kwargs): - """just wraps the default types map to set - basic constraints like `required`, `default`, etc. - """ - yamstype = DBM2Y_TYPESMAP[prop.data_type] - if 'default' not in kwargs: - default = prop.default_value() - if default is not None: - kwargs['default'] = default - if prop.required: - kwargs['required'] = True - return yamstype(**kwargs) - -def dbm2y_string_factory(prop): - """like dbm2y_default_factory but also deals with `maxsize` and `vocabulary`""" - kwargs = {} - if prop.data_type is basestring: - kwargs['maxsize'] = 500 - if prop.choices is not None: - kwargs['vocabulary'] = prop.choices - return dbm2y_default_factory(prop, **kwargs) - -def dbm2y_date_factory(prop): - """like dbm2y_default_factory but also deals with today / now definition""" - kwargs = {} - if prop.auto_now_add: - if prop.data_type is datetime: - kwargs['default'] = 'now' - else: - kwargs['default'] = 'today' - # XXX no equivalent to Django's `auto_now` - return dbm2y_default_factory(prop, **kwargs) - - -def dbm2y_relation_factory(etype, prop, multiple=False): - """called if `prop` is a `db.ReferenceProperty`""" - if multiple: - cardinality = '**' - elif prop.required: - cardinality = '1*' - else: - cardinality = '?*' - # XXX deal with potential kwargs of ReferenceProperty.__init__() - try: - return SubjectRelation(prop.data_type.kind(), cardinality=cardinality) - except AttributeError, ex: - # hack, data_type is still _SELF_REFERENCE_MARKER - return SubjectRelation(etype, cardinality=cardinality) - - -DBM2Y_FACTORY = { - basestring: dbm2y_string_factory, - datastore_types.Text: dbm2y_string_factory, - int: dbm2y_default_factory, - float: dbm2y_default_factory, - bool: dbm2y_default_factory, - time: dbm2y_date_factory, - date: dbm2y_date_factory, - datetime: dbm2y_date_factory, - datastore_types.Blob: dbm2y_default_factory, - } - - -class GaeSchemaLoader(CubicWebSchemaLoader): - """Google appengine schema loader class""" - def __init__(self, *args, **kwargs): - self.use_gauthservice = kwargs.pop('use_gauthservice', False) - super(GaeSchemaLoader, self).__init__(*args, **kwargs) - self.defined = {} - self.created = [] - self.loaded_files = [] - self._instantiate_handlers() - - def finalize(self, register_base_types=False): - return self._build_schema('google-appengine', register_base_types) - - def load_dbmodel(self, name, props): - clsdict = {} - ordered_props = sorted(props.items(), - key=lambda x: x[1].creation_counter) - for pname, prop in ordered_props: - if isinstance(prop, db.ListProperty): - if not issubclass(prop.item_type, db.Model): - self.error('ignoring list property with %s item type' - % prop.item_type) - continue - rdef = dbm2y_relation_factory(name, prop, multiple=True) - else: - try: - if isinstance(prop, (db.ReferenceProperty, - goadb.ReferencePropertyStub)): - rdef = dbm2y_relation_factory(name, prop) - else: - rdef = DBM2Y_FACTORY[prop.data_type](prop) - except KeyError, ex: - import traceback - traceback.print_exc() - self.error('ignoring property %s (keyerror on %s)' % (pname, ex)) - continue - rdef.creation_rank = prop.creation_counter - clsdict[pname] = rdef - edef = metadefinition(name, (EntityType,), clsdict) - self.add_definition(self, edef()) - - def error(self, msg): - print 'ERROR:', msg - - def import_yams_schema(self, ertype, schemamod): - erdef = self.pyreader.import_erschema(ertype, schemamod) - - def import_yams_cube_schema(self, templpath): - for filepath in self.get_schema_files(templpath): - self.handle_file(filepath) - - @property - def pyreader(self): - return self._live_handlers['.py'] - -import os -from cubicweb import CW_SOFTWARE_ROOT - -def load_schema(config, schemaclasses=None, extrahook=None): - """high level method to load all the schema for a lax instance""" - # IMPORTANT NOTE: dbmodel schemas must be imported **BEFORE** - # the loader is instantiated because this is where the dbmodels - # are registered in the yams schema - for compname in config['included-cubes']: - __import__('%s.schema' % compname) - loader = GaeSchemaLoader(use_gauthservice=config['use-google-auth'], db=db) - if schemaclasses is not None: - for cls in schemaclasses: - loader.load_dbmodel(cls.__name__, goadb.extract_dbmodel(cls)) - elif config['schema-type'] == 'dbmodel': - import schema as appschema - for obj in vars(appschema).values(): - if isinstance(obj, type) and issubclass(obj, goadb.Model) and obj.__module__ == appschema.__name__: - loader.load_dbmodel(obj.__name__, goadb.extract_dbmodel(obj)) - for erschema in ('CWGroup', 'CWEType', 'CWRType', 'RQLExpression', - 'is_', 'is_instance_of', - 'read_permission', 'add_permission', - 'delete_permission', 'update_permission'): - loader.import_yams_schema(erschema, 'bootstrap') - loader.handle_file(join(CW_SOFTWARE_ROOT, 'schemas', 'base.py')) - cubes = config['included-yams-cubes'] - for cube in reversed(config.expand_cubes(cubes)): - config.info('loading cube %s', cube) - loader.import_yams_cube_schema(config.cube_dir(cube)) - if config['schema-type'] == 'yams': - loader.import_yams_cube_schema('.') - if extrahook is not None: - extrahook(loader) - if config['use-google-auth']: - loader.defined['CWUser'].remove_relation('upassword') - loader.defined['CWUser'].permissions['add'] = () - loader.defined['CWUser'].permissions['delete'] = () - for etype in ('CWGroup', 'RQLExpression'): - read_perm_rel = loader.defined[etype].get_relations('read_permission').next() - read_perm_rel.cardinality = '**' - # XXX not yet ready for CWUser workflow - loader.defined['CWUser'].remove_relation('in_state') - loader.defined['CWUser'].remove_relation('wf_info_for') - # remove RQLConstraint('NOT O name "owners"') on CWUser in_group CWGroup - # since "owners" group is not persistent with gae - loader.defined['CWUser'].get_relations('in_group').next().constraints = [] - # return the full schema including the cubes' schema - for ertype in loader.defined.values(): - if getattr(ertype, 'inlined', False): - ertype.inlined = False - return loader.finalize() diff -r bdc3dc94d744 -r 470d8e828fda goa/doc/FAQ.en.txt --- a/goa/doc/FAQ.en.txt Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -============================== -LAX Frequently Asked Questions -============================== - -[WRITE ME] \ No newline at end of file diff -r bdc3dc94d744 -r 470d8e828fda goa/doc/README_LAX.fr.txt --- a/goa/doc/README_LAX.fr.txt Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -Qu'est-ce que ``LAX`` ? -======================= - -``LAX`` (Logilab Application engine eXtension) est un framework -d'application web qui facilite les développements faits pour -``Google AppEngine``. - -``LAX`` est un portage de la partie web de la plate-forme -applicative développée par Logilab depuis 2001. Cette plate-forme -publie des données que la partie stockage tire de bases SQL, -d'annuaires LDAP et de systèmes de gestion de version. Depuis mai -2008, elle fonctionne sur le "datastore" de ``Google AppEngine``. - -``LAX`` est pour le moment en version alpha. - -Django/GAE vs. LAX/GAE -======================= - -NotImplementedError() - - -Téléchargement des sources -========================== - -- Les sources de ``Google AppEngine`` peuvent être obtenues à l'adresse - suivante : http://code.google.com/appengine/downloads.html - -- Les sources de ``LAX`` se trouvent à l'adresse suivante : - http://lax.logilab.org/ - - -Installation -============ - -Les sources de ``Google AppEngine`` doivent être décompressées et le -répertoire `google` qui s'y trouve doit être accessible par la variable -d'environnement ``PYTHONPATH``. Correctement définir le ``PYTHONPATH`` -n'est pas nécessaire pour le lancement de l'application elle-même mais -pour l'utilisation des scripts fournis par ``LAX`` ou pour l'exécution -des tests unitaires. - -Une fois décompactée, l'archive ``lax-0.1.0-alpha.tar.gz``, on obtient -l'arborescence suivante:: - - . - |-- app.yaml - |-- custom.py - |-- data - |-- cubicweb/ - |-- i18n/ - |-- logilab/ - |-- main.py - |-- mx/ - |-- rql/ - |-- schema.py - |-- simplejson/ - |-- tools/ - | |-- generate_schema_img.py - | `-- i18ncompile.py - |-- views.py - |-- yams/ - `-- yapps/ - - -On retrouve le squelette d'une application web de ``Google AppEngine`` -(fichiers ``app.yaml``, ``main.py``en particulier) avec les dépendances -supplémentaires nécessaires à l'utilisation du framework ``LAX`` - - -Lancement de l'application de base -================================== - -python /path/to/google_appengine/dev_appserver.py /path/to/lax - - diff -r bdc3dc94d744 -r 470d8e828fda goa/doc/devmanual_fr/advanced_notes.txt --- a/goa/doc/devmanual_fr/advanced_notes.txt Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ - -La différence entre la classe `AppRsetObject` et la classe `AppObject` est que -les instances de la premières sont séléctionnées pour une requête et un "result -set" et alors que les secondes ne sont séléctionnées qu'en fonction de leur -identifiant. diff -r bdc3dc94d744 -r 470d8e828fda goa/doc/devmanual_fr/archi_globale.dia Binary file goa/doc/devmanual_fr/archi_globale.dia has changed diff -r bdc3dc94d744 -r 470d8e828fda goa/doc/devmanual_fr/archi_globale.png Binary file goa/doc/devmanual_fr/archi_globale.png has changed diff -r bdc3dc94d744 -r 470d8e828fda goa/doc/devmanual_fr/chap_autres_composants_ui.txt --- a/goa/doc/devmanual_fr/chap_autres_composants_ui.txt Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -Autres composants de l'interface web -==================================== - -Actions -------- -XXXFILLME - -Component, VComponent ---------------------- -XXXFILLME - -CWProperty ---------- -XXXFILLME diff -r bdc3dc94d744 -r 470d8e828fda goa/doc/devmanual_fr/chap_bases_framework_erudi.txt --- a/goa/doc/devmanual_fr/chap_bases_framework_erudi.txt Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,226 +0,0 @@ -Fondements du framework CubicWeb -============================= - -Le moteur web d'cubicweb consiste en quelques classes gérant un ensemble d'objets -chargés dynamiquement au lancement d'cubicweb. Ce sont ces objets dynamiques, issus -du modèle ou de la librairie, qui construisent le site web final. Les différents -composants dynamiques sont par exemple : - -* coté client et serveur - - - les définitions d'entités, contenant la logique permettant la manipulation des - données de l'application - -* coté client - - - les *vues* , ou encore plus spécifiquement - - - les boites - - l'en-tête et le pied de page - - les formulaires - - les gabarits de pages - - - les *actions* - - les *controleurs* - -* coté serveur - - - les crochets de notification - - les vues de notification - -Les différents composants du moteur sont : - -* un frontal web (seul twisted disponible pour le moment), transparent du point - de vue des objets dynamiques -* un objet encapsulant la configuration -* un `vregistry` (`cubicweb.cwvreg`) contenant les objets chargés dynamiquements - - -Détail de la procédure d'enregistrement ---------------------------------------- -Au démarage le `vregistry` ou base de registres inspecte un certain nombre de -répertoires à la recherche de définition de classes "compatible". Après une -procédure d'enregistrement les objets sont affectés dans différents registres -afin d'être ensuite séléctionné dynamiquement pendant le fonctionnement de -l'application. - -La classe de base de tout ces objets est la classe `AppRsetObject` (module -`cubicweb.common.appobject`). - - -API Python/RQL --------------- - -Inspiré de la db-api standard, avec un object Connection possédant les méthodes -cursor, rollback et commit principalement. La méthode importante est la méthode -`execute` du curseur : - -`execute(rqlstring, args=None, eid_key=None, build_descr=True)` - -:rqlstring: la requête rql à éxécuter (unicode) -:args: si la requête contient des substitutions, un dictionnaire contenant les - valeurs à utiliser -:eid_key: - un détail d'implémentation du cache de requêtes RQL fait que si une substitution est - utilisée pour introduire un eid *levant des ambiguités dans la résolution de - type de la requête*, il faut spécifier par cet argument la clé correspondante - dans le dictionnaire - -C'est l'objet Connection qui possède les méthodes classiques `commit` et -`rollback`. Vous ne *devriez jamais avoir à les utiliser* lors du développement -d'interface web sur la base du framework CubicWeb étant donné que la fin de la -transaction est déterminée par celui-ci en fonction du succès d'éxécution de la -requête. - -NOTE : lors de l'éxécution de requêtes de modification (SET,INSERT,DELETE), si une -requête génère une erreur liée à la sécurité, un rollback est systématiquement -effectuée sur la transaction courante. - - -La classe `Request` (`cubicweb.web`) ---------------------------------- -Une instance de requête est créée lorsque une requête HTTP est transmise au -serveur web. Elle contient des informations telles que les paramètres de -formulaires, l'utilisateur connecté, etc. - -**De manière plus générale une requête représente une demande d'un utilisateur, -que se soit par HTTP ou non (on parle également de requête rql coté serveur par -exemple)** - -Une instance de la classe `Request` possède les attributs : - -* `user`, instance de`cubicweb.common.utils.User` correspondant à l'utilisateur - connecté -* `form`, dictionaire contenant les valeurs de formulaire web -* `encoding`, l'encodage de caractère à utiliser dans la réponse - -Mais encore : - -:Gestion des données de session: - * `session_data()`, retourne un dictionaire contenant l'intégralité des - données de la session - * `get_session_data(key, default=None)`, retourne la valeur associée à - la clé ou la valeur `default` si la clé n'est pas définie - * `set_session_data(key, value)`, associe une valeur à une clé - * `del_session_data(key)`, supprime la valeur associé à une clé - - -:Gestion de cookie: - * `get_cookie()`, retourne un dictionnaire contenant la valeur de l'entête - HTTP 'Cookie' - * `set_cookie(cookie, key, maxage=300)`, ajoute un en-tête HTTP `Set-Cookie`, - avec une durée de vie 5 minutes par défault (`maxage` = None donne un cooke - *de session"* expirant quand l'utilisateur ferme son navigateur - * `remove_cookie(cookie, key)`, fait expirer une valeur - -:Gestion d'URL: - * `url()`, retourne l'url complète de la requête HTTP - * `base_url()`, retourne l'url de la racine de l'application - * `relative_path()`, retourne chemin relatif de la requête - -:Et encore...: - * `set_content_type(content_type, filename=None)`, place l'en-tête HTTP - 'Content-Type' - * `get_header(header)`, retourne la valeur associé à un en-tête HTTP - arbitraire de la requête - * `set_header(header, value)`, ajoute un en-tête HTTP arbitraire dans la - réponse - * `cursor()` retourne un curseur RQL sur la session - * `execute(*args, **kwargs)`, raccourci vers .cursor().execute() - * `property_value(key)`, gestion des propriétés (`CWProperty`) - * le dictionaire `data` pour stocker des données pour partager de - l'information entre les composants *durant l'éxécution de la requête*. - -A noter que cette classe est en réalité abstraite et qu'une implémentation -concrète sera fournie par le *frontend* web utilisé (en l'occurent *twisted* -aujourd'hui). Enfin pour les vues ou autres qui sont éxécutés coté serveur, -la majeure partie de l'interface de `Request` est définie sur la session -associée au client. - - -La classe `AppObject` ---------------------- - -En général : - -* on n'hérite pas directement des cette classe mais plutôt d'une classe - plus spécifique comme par exemple `AnyEntity`, `EntityView`, `AnyRsetView`, - `Action`... - -* pour être enregistrable, un classe fille doit définir son registre (attribut - `__registry__`) et son identifiant (attribut `id`). Généralement on n'a pas à - s'occuper du registre, uniquement de l'identifiant `id` :) - -On trouve un certain nombre d'attributs et de méthodes définis dans cette classe -et donc commune à tous les objets de l'application : - -A l'enregistrement, les attributs suivants sont ajoutés dynamiquement aux -*classes* filles: - -* `vreg`, le `vregistry` de l'application -* `schema`, le schéma de l'application -* `config`, la configuration de l'application - -On trouve également sur les instances les attributs : - -* `req`, instance de `Request` -* `rset`, le "result set" associé à l'objet le cas échéant -* `cursor`, curseur rql sur la session - - -:Gestion d'URL: - * `build_url(method=None, **kwargs)`, retourne une URL absolue construites à - partir des arguments donnés. Le *controleur* devant gérer la réponse - peut-être spécifié via l'argument spécial `method` (le branchement est - théoriquement bien effectué automatiquement :). - - * `datadir_url()`, retourne l'url du répertoire de données de l'application - (contenant les fichiers statiques tels que les images, css, js...) - - * `base_url()`, raccourci sur `req.base_url()` - - * `url_quote(value)`, version *unicode safe* de de la fonction `urllib.quote` - -:Manipulation de données: - - * `etype_rset(etype, size=1)`, raccourci vers `vreg.etype_rset()` - - * `eid_rset(eid, rql=None, descr=True)`, retourne un objet result set pour - l'eid donné - * `entity(row, col=0)`, retourne l'entité correspondant à la position données - du "result set" associé à l'objet - - * `complete_entity(row, col=0, skip_bytes=True)`, équivalent à `entity` mais - appelle également la méthode `complete()` sur l'entité avant de la retourner - -:Formattage de données: - * `format_date(date, date_format=None, time=False)` - * `format_time(time)`, - -:Et encore...: - - * `external_resource(rid, default=_MARKER)`, accède à une valeur définie dans - le fichier de configuration `external_resource` - - * `tal_render(template, variables)`, - - -**NOTE IMPORTANTE** -Lorsqu'on hérite d'`AppObject` (même indirectement), il faut **toujours** -utiliser **super()** pour récupérer les méthodes et attributs des classes -parentes, et pas passer par l'identifiant de classe parente directement. -(sous peine de tomber sur des bugs bizarres lors du rechargement automatique -des vues). Par exemple, plutôt que d'écrire:: - - class Truc(PrimaryView): - def f(self, arg1): - PrimaryView.f(self, arg1) - -Il faut écrire:: - - class Truc(PrimaryView): - def f(self, arg1): - super(Truc, self).f(arg1) - - -XXX FILLME diagramme interaction application/controller/template/view diff -r bdc3dc94d744 -r 470d8e828fda goa/doc/devmanual_fr/chap_configuration_instance.txt --- a/goa/doc/devmanual_fr/chap_configuration_instance.txt Fri Sep 24 18:20:57 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,160 +0,0 @@ -Configuration d'une instance -============================ - -À la création d'une instance, un fichier de configuration est généré dans :: - - $(CW_REGISTRY)//.conf - -par exemple :: - - /etc/cubicweb.d/jpl/all-in-one.conf - -C'est un simple fichier texte au format INI. Dans la description suivante, -chaque nom d'option est préfixé de sa section et suivi de sa valeur par défaut -le cas échéant, e.g. "`
.