Michael Merickel
2018-10-19 d579f2104de139e0b0fc5d6c81aabb2f826e5e54
commit | author | age
e4c057 1 import unittest
MM 2
3 from pyramid.exceptions import ConfigurationConflictError
4 from pyramid.exceptions import ConfigurationExecutionError
5
6 from pyramid.interfaces import IRequest
7
8
9 class ConfiguratorTests(unittest.TestCase):
10     def _makeOne(self, *arg, **kw):
11         from pyramid.config import Configurator
12
13         config = Configurator(*arg, **kw)
14         return config
15
16     def _getViewCallable(
17         self,
18         config,
19         ctx_iface=None,
20         request_iface=None,
21         name='',
22         exception_view=False,
23     ):
24         from zope.interface import Interface
25         from pyramid.interfaces import IView
26         from pyramid.interfaces import IViewClassifier
27         from pyramid.interfaces import IExceptionViewClassifier
28
29         if exception_view:  # pragma: no cover
30             classifier = IExceptionViewClassifier
31         else:
32             classifier = IViewClassifier
33         if ctx_iface is None:
34             ctx_iface = Interface
35         if request_iface is None:
36             request_iface = IRequest
37         return config.registry.adapters.lookup(
38             (classifier, request_iface, ctx_iface),
39             IView,
40             name=name,
41             default=None,
42         )
43
44     def test_action_branching_kw_is_None(self):
45         config = self._makeOne(autocommit=True)
46         self.assertEqual(config.action('discrim'), None)
47
48     def test_action_branching_kw_is_not_None(self):
49         config = self._makeOne(autocommit=True)
50         self.assertEqual(config.action('discrim', kw={'a': 1}), None)
51
52     def test_action_autocommit_with_introspectables(self):
d579f2 53         from pyramid.config.actions import ActionInfo
e4c057 54
MM 55         config = self._makeOne(autocommit=True)
56         intr = DummyIntrospectable()
57         config.action('discrim', introspectables=(intr,))
58         self.assertEqual(len(intr.registered), 1)
59         self.assertEqual(intr.registered[0][0], config.introspector)
60         self.assertEqual(intr.registered[0][1].__class__, ActionInfo)
61
62     def test_action_autocommit_with_introspectables_introspection_off(self):
63         config = self._makeOne(autocommit=True)
64         config.introspection = False
65         intr = DummyIntrospectable()
66         config.action('discrim', introspectables=(intr,))
67         self.assertEqual(len(intr.registered), 0)
68
69     def test_action_branching_nonautocommit_with_config_info(self):
70         config = self._makeOne(autocommit=False)
71         config.info = 'abc'
72         state = DummyActionState()
73         state.autocommit = False
74         config.action_state = state
75         config.action('discrim', kw={'a': 1})
76         self.assertEqual(
77             state.actions,
78             [
79                 (
80                     (),
81                     {
82                         'args': (),
83                         'callable': None,
84                         'discriminator': 'discrim',
85                         'includepath': (),
86                         'info': 'abc',
87                         'introspectables': (),
88                         'kw': {'a': 1},
89                         'order': 0,
90                     },
91                 )
92             ],
93         )
94
95     def test_action_branching_nonautocommit_without_config_info(self):
96         config = self._makeOne(autocommit=False)
97         config.info = ''
98         config._ainfo = ['z']
99         state = DummyActionState()
100         config.action_state = state
101         state.autocommit = False
102         config.action('discrim', kw={'a': 1})
103         self.assertEqual(
104             state.actions,
105             [
106                 (
107                     (),
108                     {
109                         'args': (),
110                         'callable': None,
111                         'discriminator': 'discrim',
112                         'includepath': (),
113                         'info': 'z',
114                         'introspectables': (),
115                         'kw': {'a': 1},
116                         'order': 0,
117                     },
118                 )
119             ],
120         )
121
122     def test_action_branching_nonautocommit_with_introspectables(self):
123         config = self._makeOne(autocommit=False)
124         config.info = ''
125         config._ainfo = []
126         state = DummyActionState()
127         config.action_state = state
128         state.autocommit = False
129         intr = DummyIntrospectable()
130         config.action('discrim', introspectables=(intr,))
131         self.assertEqual(state.actions[0][1]['introspectables'], (intr,))
132
133     def test_action_nonautocommit_with_introspectables_introspection_off(self):
134         config = self._makeOne(autocommit=False)
135         config.info = ''
136         config._ainfo = []
137         config.introspection = False
138         state = DummyActionState()
139         config.action_state = state
140         state.autocommit = False
141         intr = DummyIntrospectable()
142         config.action('discrim', introspectables=(intr,))
143         self.assertEqual(state.actions[0][1]['introspectables'], ())
144
145     def test_commit_conflict_simple(self):
146         config = self._makeOne()
147
148         def view1(request):  # pragma: no cover
149             pass
150
151         def view2(request):  # pragma: no cover
152             pass
153
154         config.add_view(view1)
155         config.add_view(view2)
156         self.assertRaises(ConfigurationConflictError, config.commit)
157
158     def test_commit_conflict_resolved_with_include(self):
159         config = self._makeOne()
160
161         def view1(request):  # pragma: no cover
162             pass
163
164         def view2(request):  # pragma: no cover
165             pass
166
167         def includeme(config):
168             config.add_view(view2)
169
170         config.add_view(view1)
171         config.include(includeme)
172         config.commit()
173         registeredview = self._getViewCallable(config)
174         self.assertEqual(registeredview.__name__, 'view1')
175
176     def test_commit_conflict_with_two_includes(self):
177         config = self._makeOne()
178
179         def view1(request):  # pragma: no cover
180             pass
181
182         def view2(request):  # pragma: no cover
183             pass
184
185         def includeme1(config):
186             config.add_view(view1)
187
188         def includeme2(config):
189             config.add_view(view2)
190
191         config.include(includeme1)
192         config.include(includeme2)
193         try:
194             config.commit()
195         except ConfigurationConflictError as why:
196             c1, c2 = _conflictFunctions(why)
197             self.assertEqual(c1, 'includeme1')
198             self.assertEqual(c2, 'includeme2')
199         else:  # pragma: no cover
200             raise AssertionError
201
202     def test_commit_conflict_resolved_with_two_includes_and_local(self):
203         config = self._makeOne()
204
205         def view1(request):  # pragma: no cover
206             pass
207
208         def view2(request):  # pragma: no cover
209             pass
210
211         def view3(request):  # pragma: no cover
212             pass
213
214         def includeme1(config):
215             config.add_view(view1)
216
217         def includeme2(config):
218             config.add_view(view2)
219
220         config.include(includeme1)
221         config.include(includeme2)
222         config.add_view(view3)
223         config.commit()
224         registeredview = self._getViewCallable(config)
225         self.assertEqual(registeredview.__name__, 'view3')
226
227     def test_autocommit_no_conflicts(self):
228         from pyramid.renderers import null_renderer
229
230         config = self._makeOne(autocommit=True)
231
232         def view1(request):  # pragma: no cover
233             pass
234
235         def view2(request):  # pragma: no cover
236             pass
237
238         def view3(request):  # pragma: no cover
239             pass
240
241         config.add_view(view1, renderer=null_renderer)
242         config.add_view(view2, renderer=null_renderer)
243         config.add_view(view3, renderer=null_renderer)
244         config.commit()
245         registeredview = self._getViewCallable(config)
246         self.assertEqual(registeredview.__name__, 'view3')
247
248     def test_conflict_set_notfound_view(self):
249         config = self._makeOne()
250
251         def view1(request):  # pragma: no cover
252             pass
253
254         def view2(request):  # pragma: no cover
255             pass
256
257         config.set_notfound_view(view1)
258         config.set_notfound_view(view2)
259         try:
260             config.commit()
261         except ConfigurationConflictError as why:
262             c1, c2 = _conflictFunctions(why)
263             self.assertEqual(c1, 'test_conflict_set_notfound_view')
264             self.assertEqual(c2, 'test_conflict_set_notfound_view')
265         else:  # pragma: no cover
266             raise AssertionError
267
268     def test_conflict_set_forbidden_view(self):
269         config = self._makeOne()
270
271         def view1(request):  # pragma: no cover
272             pass
273
274         def view2(request):  # pragma: no cover
275             pass
276
277         config.set_forbidden_view(view1)
278         config.set_forbidden_view(view2)
279         try:
280             config.commit()
281         except ConfigurationConflictError as why:
282             c1, c2 = _conflictFunctions(why)
283             self.assertEqual(c1, 'test_conflict_set_forbidden_view')
284             self.assertEqual(c2, 'test_conflict_set_forbidden_view')
285         else:  # pragma: no cover
286             raise AssertionError
287
288
289 class TestActionState(unittest.TestCase):
290     def _makeOne(self):
291         from pyramid.config.actions import ActionState
292
293         return ActionState()
294
295     def test_it(self):
296         c = self._makeOne()
297         self.assertEqual(c.actions, [])
298
299     def test_action_simple(self):
300         from . import dummyfactory as f
301
302         c = self._makeOne()
303         c.actions = []
304         c.action(1, f, (1,), {'x': 1})
305         self.assertEqual(
306             c.actions,
307             [
308                 {
309                     'args': (1,),
310                     'callable': f,
311                     'discriminator': 1,
312                     'includepath': (),
313                     'info': None,
314                     'introspectables': (),
315                     'kw': {'x': 1},
316                     'order': 0,
317                 }
318             ],
319         )
320         c.action(None)
321         self.assertEqual(
322             c.actions,
323             [
324                 {
325                     'args': (1,),
326                     'callable': f,
327                     'discriminator': 1,
328                     'includepath': (),
329                     'info': None,
330                     'introspectables': (),
331                     'kw': {'x': 1},
332                     'order': 0,
333                 },
334                 {
335                     'args': (),
336                     'callable': None,
337                     'discriminator': None,
338                     'includepath': (),
339                     'info': None,
340                     'introspectables': (),
341                     'kw': {},
342                     'order': 0,
343                 },
344             ],
345         )
346
347     def test_action_with_includepath(self):
348         c = self._makeOne()
349         c.actions = []
350         c.action(None, includepath=('abc',))
351         self.assertEqual(
352             c.actions,
353             [
354                 {
355                     'args': (),
356                     'callable': None,
357                     'discriminator': None,
358                     'includepath': ('abc',),
359                     'info': None,
360                     'introspectables': (),
361                     'kw': {},
362                     'order': 0,
363                 }
364             ],
365         )
366
367     def test_action_with_info(self):
368         c = self._makeOne()
369         c.action(None, info='abc')
370         self.assertEqual(
371             c.actions,
372             [
373                 {
374                     'args': (),
375                     'callable': None,
376                     'discriminator': None,
377                     'includepath': (),
378                     'info': 'abc',
379                     'introspectables': (),
380                     'kw': {},
381                     'order': 0,
382                 }
383             ],
384         )
385
386     def test_action_with_includepath_and_info(self):
387         c = self._makeOne()
388         c.action(None, includepath=('spec',), info='bleh')
389         self.assertEqual(
390             c.actions,
391             [
392                 {
393                     'args': (),
394                     'callable': None,
395                     'discriminator': None,
396                     'includepath': ('spec',),
397                     'info': 'bleh',
398                     'introspectables': (),
399                     'kw': {},
400                     'order': 0,
401                 }
402             ],
403         )
404
405     def test_action_with_order(self):
406         c = self._makeOne()
407         c.actions = []
408         c.action(None, order=99999)
409         self.assertEqual(
410             c.actions,
411             [
412                 {
413                     'args': (),
414                     'callable': None,
415                     'discriminator': None,
416                     'includepath': (),
417                     'info': None,
418                     'introspectables': (),
419                     'kw': {},
420                     'order': 99999,
421                 }
422             ],
423         )
424
425     def test_action_with_introspectables(self):
426         c = self._makeOne()
427         c.actions = []
428         intr = DummyIntrospectable()
429         c.action(None, introspectables=(intr,))
430         self.assertEqual(
431             c.actions,
432             [
433                 {
434                     'args': (),
435                     'callable': None,
436                     'discriminator': None,
437                     'includepath': (),
438                     'info': None,
439                     'introspectables': (intr,),
440                     'kw': {},
441                     'order': 0,
442                 }
443             ],
444         )
445
446     def test_processSpec(self):
447         c = self._makeOne()
448         self.assertTrue(c.processSpec('spec'))
449         self.assertFalse(c.processSpec('spec'))
450
451     def test_execute_actions_tuples(self):
452         output = []
453
454         def f(*a, **k):
455             output.append((a, k))
456
457         c = self._makeOne()
458         c.actions = [
459             (1, f, (1,)),
460             (1, f, (11,), {}, ('x',)),
461             (2, f, (2,)),
462             (None, None),
463         ]
464         c.execute_actions()
465         self.assertEqual(output, [((1,), {}), ((2,), {})])
466
467     def test_execute_actions_dicts(self):
468         output = []
469
470         def f(*a, **k):
471             output.append((a, k))
472
473         c = self._makeOne()
474         c.actions = [
475             {
476                 'discriminator': 1,
477                 'callable': f,
478                 'args': (1,),
479                 'kw': {},
480                 'order': 0,
481                 'includepath': (),
482                 'info': None,
483                 'introspectables': (),
484             },
485             {
486                 'discriminator': 1,
487                 'callable': f,
488                 'args': (11,),
489                 'kw': {},
490                 'includepath': ('x',),
491                 'order': 0,
492                 'info': None,
493                 'introspectables': (),
494             },
495             {
496                 'discriminator': 2,
497                 'callable': f,
498                 'args': (2,),
499                 'kw': {},
500                 'order': 0,
501                 'includepath': (),
502                 'info': None,
503                 'introspectables': (),
504             },
505             {
506                 'discriminator': None,
507                 'callable': None,
508                 'args': (),
509                 'kw': {},
510                 'order': 0,
511                 'includepath': (),
512                 'info': None,
513                 'introspectables': (),
514             },
515         ]
516         c.execute_actions()
517         self.assertEqual(output, [((1,), {}), ((2,), {})])
518
519     def test_execute_actions_with_introspectables(self):
520         output = []
521
522         def f(*a, **k):
523             output.append((a, k))
524
525         c = self._makeOne()
526         intr = DummyIntrospectable()
527         c.actions = [
528             {
529                 'discriminator': 1,
530                 'callable': f,
531                 'args': (1,),
532                 'kw': {},
533                 'order': 0,
534                 'includepath': (),
535                 'info': None,
536                 'introspectables': (intr,),
537             }
538         ]
539         introspector = object()
540         c.execute_actions(introspector=introspector)
541         self.assertEqual(output, [((1,), {})])
542         self.assertEqual(intr.registered, [(introspector, None)])
543
544     def test_execute_actions_with_introspectable_no_callable(self):
545         c = self._makeOne()
546         intr = DummyIntrospectable()
547         c.actions = [
548             {
549                 'discriminator': 1,
550                 'callable': None,
551                 'args': (1,),
552                 'kw': {},
553                 'order': 0,
554                 'includepath': (),
555                 'info': None,
556                 'introspectables': (intr,),
557             }
558         ]
559         introspector = object()
560         c.execute_actions(introspector=introspector)
561         self.assertEqual(intr.registered, [(introspector, None)])
562
563     def test_execute_actions_error(self):
564         output = []
565
566         def f(*a, **k):
567             output.append(('f', a, k))
568
569         def bad():
570             raise NotImplementedError
571
572         c = self._makeOne()
573         c.actions = [
574             (1, f, (1,)),
575             (1, f, (11,), {}, ('x',)),
576             (2, f, (2,)),
577             (3, bad, (), {}, (), 'oops'),
578         ]
579         self.assertRaises(ConfigurationExecutionError, c.execute_actions)
580         self.assertEqual(output, [('f', (1,), {}), ('f', (2,), {})])
581
582     def test_reentrant_action(self):
583         output = []
584         c = self._makeOne()
585
586         def f(*a, **k):
587             output.append(('f', a, k))
588             c.actions.append((3, g, (8,), {}))
589
590         def g(*a, **k):
591             output.append(('g', a, k))
592
593         c.actions = [(1, f, (1,))]
594         c.execute_actions()
595         self.assertEqual(output, [('f', (1,), {}), ('g', (8,), {})])
596
597     def test_reentrant_action_with_deferred_discriminator(self):
598         # see https://github.com/Pylons/pyramid/issues/2697
599         from pyramid.registry import Deferred
600
601         output = []
602         c = self._makeOne()
603
604         def f(*a, **k):
605             output.append(('f', a, k))
606             c.actions.append((4, g, (4,), {}, (), None, 2))
607
608         def g(*a, **k):
609             output.append(('g', a, k))
610
611         def h(*a, **k):
612             output.append(('h', a, k))
613
614         def discrim():
615             self.assertEqual(output, [('f', (1,), {}), ('g', (2,), {})])
616             return 3
617
618         d = Deferred(discrim)
619         c.actions = [
620             (d, h, (3,), {}, (), None, 1),  # order 1
621             (1, f, (1,)),  # order 0
622             (2, g, (2,)),  # order 0
623         ]
624         c.execute_actions()
625         self.assertEqual(
626             output,
627             [
628                 ('f', (1,), {}),
629                 ('g', (2,), {}),
630                 ('h', (3,), {}),
631                 ('g', (4,), {}),
632             ],
633         )
634
635     def test_reentrant_action_error(self):
636         from pyramid.exceptions import ConfigurationError
637
638         c = self._makeOne()
639
640         def f(*a, **k):
641             c.actions.append((3, g, (8,), {}, (), None, -1))
642
643         def g(*a, **k):  # pragma: no cover
644             pass
645
646         c.actions = [(1, f, (1,))]
647         self.assertRaises(ConfigurationError, c.execute_actions)
648
649     def test_reentrant_action_without_clear(self):
650         c = self._makeOne()
651
652         def f(*a, **k):
653             c.actions.append((3, g, (8,)))
654
655         def g(*a, **k):
656             pass
657
658         c.actions = [(1, f, (1,))]
659         c.execute_actions(clear=False)
660         self.assertEqual(c.actions, [(1, f, (1,)), (3, g, (8,))])
661
662     def test_executing_conflicting_action_across_orders(self):
663         from pyramid.exceptions import ConfigurationConflictError
664
665         c = self._makeOne()
666
667         def f(*a, **k):
668             pass
669
670         def g(*a, **k):  # pragma: no cover
671             pass
672
673         c.actions = [(1, f, (1,), {}, (), None, -1), (1, g, (2,))]
674         self.assertRaises(ConfigurationConflictError, c.execute_actions)
675
676     def test_executing_conflicting_action_across_reentrant_orders(self):
677         from pyramid.exceptions import ConfigurationConflictError
678
679         c = self._makeOne()
680
681         def f(*a, **k):
682             c.actions.append((1, g, (8,)))
683
684         def g(*a, **k):  # pragma: no cover
685             pass
686
687         c.actions = [(1, f, (1,), {}, (), None, -1)]
688         self.assertRaises(ConfigurationConflictError, c.execute_actions)
689
690
691 class Test_reentrant_action_functional(unittest.TestCase):
692     def _makeConfigurator(self, *arg, **kw):
693         from pyramid.config import Configurator
694
695         config = Configurator(*arg, **kw)
696         return config
697
698     def test_functional(self):
699         def add_auto_route(config, name, view):
700             def register():
701                 config.add_view(route_name=name, view=view)
702                 config.add_route(name, '/' + name)
703
704             config.action(('auto route', name), register, order=-30)
705
706         config = self._makeConfigurator()
707         config.add_directive('add_auto_route', add_auto_route)
708
709         def my_view(request):  # pragma: no cover
710             return request.response
711
712         config.add_auto_route('foo', my_view)
713         config.commit()
714         from pyramid.interfaces import IRoutesMapper
715
716         mapper = config.registry.getUtility(IRoutesMapper)
717         routes = mapper.get_routes()
718         route = routes[0]
719         self.assertEqual(len(routes), 1)
720         self.assertEqual(route.name, 'foo')
721         self.assertEqual(route.path, '/foo')
722
723     def test_deferred_discriminator(self):
724         # see https://github.com/Pylons/pyramid/issues/2697
725         from pyramid.config import PHASE0_CONFIG
726
727         config = self._makeConfigurator()
728
729         def deriver(view, info):
730             return view
731
732         deriver.options = ('foo',)
733         config.add_view_deriver(deriver, 'foo_view')
734         # add_view uses a deferred discriminator and will fail if executed
735         # prior to add_view_deriver executing its action
736         config.add_view(lambda r: r.response, name='', foo=1)
737
738         def dummy_action():
739             # trigger a re-entrant action
740             config.action(None, lambda: None)
741
742         config.action(None, dummy_action, order=PHASE0_CONFIG)
743         config.commit()
744
745
746 class Test_resolveConflicts(unittest.TestCase):
747     def _callFUT(self, actions):
748         from pyramid.config.actions import resolveConflicts
749
750         return resolveConflicts(actions)
751
752     def test_it_success_tuples(self):
753         from . import dummyfactory as f
754
755         result = self._callFUT(
756             [
757                 (None, f),
758                 (1, f, (1,), {}, (), 'first'),
759                 (1, f, (2,), {}, ('x',), 'second'),
760                 (1, f, (3,), {}, ('y',), 'third'),
761                 (4, f, (4,), {}, ('y',), 'should be last', 99999),
762                 (3, f, (3,), {}, ('y',)),
763                 (None, f, (5,), {}, ('y',)),
764             ]
765         )
766         result = list(result)
767         self.assertEqual(
768             result,
769             [
770                 {
771                     'info': None,
772                     'args': (),
773                     'callable': f,
774                     'introspectables': (),
775                     'kw': {},
776                     'discriminator': None,
777                     'includepath': (),
778                     'order': 0,
779                 },
780                 {
781                     'info': 'first',
782                     'args': (1,),
783                     'callable': f,
784                     'introspectables': (),
785                     'kw': {},
786                     'discriminator': 1,
787                     'includepath': (),
788                     'order': 0,
789                 },
790                 {
791                     'info': None,
792                     'args': (3,),
793                     'callable': f,
794                     'introspectables': (),
795                     'kw': {},
796                     'discriminator': 3,
797                     'includepath': ('y',),
798                     'order': 0,
799                 },
800                 {
801                     'info': None,
802                     'args': (5,),
803                     'callable': f,
804                     'introspectables': (),
805                     'kw': {},
806                     'discriminator': None,
807                     'includepath': ('y',),
808                     'order': 0,
809                 },
810                 {
811                     'info': 'should be last',
812                     'args': (4,),
813                     'callable': f,
814                     'introspectables': (),
815                     'kw': {},
816                     'discriminator': 4,
817                     'includepath': ('y',),
818                     'order': 99999,
819                 },
820             ],
821         )
822
823     def test_it_success_dicts(self):
824         from . import dummyfactory as f
825
826         result = self._callFUT(
827             [
828                 (None, f),
829                 (1, f, (1,), {}, (), 'first'),
830                 (1, f, (2,), {}, ('x',), 'second'),
831                 (1, f, (3,), {}, ('y',), 'third'),
832                 (4, f, (4,), {}, ('y',), 'should be last', 99999),
833                 (3, f, (3,), {}, ('y',)),
834                 (None, f, (5,), {}, ('y',)),
835             ]
836         )
837         result = list(result)
838         self.assertEqual(
839             result,
840             [
841                 {
842                     'info': None,
843                     'args': (),
844                     'callable': f,
845                     'introspectables': (),
846                     'kw': {},
847                     'discriminator': None,
848                     'includepath': (),
849                     'order': 0,
850                 },
851                 {
852                     'info': 'first',
853                     'args': (1,),
854                     'callable': f,
855                     'introspectables': (),
856                     'kw': {},
857                     'discriminator': 1,
858                     'includepath': (),
859                     'order': 0,
860                 },
861                 {
862                     'info': None,
863                     'args': (3,),
864                     'callable': f,
865                     'introspectables': (),
866                     'kw': {},
867                     'discriminator': 3,
868                     'includepath': ('y',),
869                     'order': 0,
870                 },
871                 {
872                     'info': None,
873                     'args': (5,),
874                     'callable': f,
875                     'introspectables': (),
876                     'kw': {},
877                     'discriminator': None,
878                     'includepath': ('y',),
879                     'order': 0,
880                 },
881                 {
882                     'info': 'should be last',
883                     'args': (4,),
884                     'callable': f,
885                     'introspectables': (),
886                     'kw': {},
887                     'discriminator': 4,
888                     'includepath': ('y',),
889                     'order': 99999,
890                 },
891             ],
892         )
893
894     def test_it_conflict(self):
895         from . import dummyfactory as f
896
897         result = self._callFUT(
898             [
899                 (None, f),
900                 (1, f, (2,), {}, ('x',), 'eek'),  # will conflict
901                 (1, f, (3,), {}, ('y',), 'ack'),  # will conflict
902                 (4, f, (4,), {}, ('y',)),
903                 (3, f, (3,), {}, ('y',)),
904                 (None, f, (5,), {}, ('y',)),
905             ]
906         )
907         self.assertRaises(ConfigurationConflictError, list, result)
908
909     def test_it_with_actions_grouped_by_order(self):
910         from . import dummyfactory as f
911
912         result = self._callFUT(
913             [
914                 (None, f),  # X
915                 (1, f, (1,), {}, (), 'third', 10),  # X
916                 (1, f, (2,), {}, ('x',), 'fourth', 10),
917                 (1, f, (3,), {}, ('y',), 'fifth', 10),
918                 (2, f, (1,), {}, (), 'sixth', 10),  # X
919                 (3, f, (1,), {}, (), 'seventh', 10),  # X
920                 (5, f, (4,), {}, ('y',), 'eighth', 99999),  # X
921                 (4, f, (3,), {}, (), 'first', 5),  # X
922                 (4, f, (5,), {}, ('y',), 'second', 5),
923             ]
924         )
925         result = list(result)
926         self.assertEqual(len(result), 6)
927         # resolved actions should be grouped by (order, i)
928         self.assertEqual(
929             result,
930             [
931                 {
932                     'info': None,
933                     'args': (),
934                     'callable': f,
935                     'introspectables': (),
936                     'kw': {},
937                     'discriminator': None,
938                     'includepath': (),
939                     'order': 0,
940                 },
941                 {
942                     'info': 'first',
943                     'args': (3,),
944                     'callable': f,
945                     'introspectables': (),
946                     'kw': {},
947                     'discriminator': 4,
948                     'includepath': (),
949                     'order': 5,
950                 },
951                 {
952                     'info': 'third',
953                     'args': (1,),
954                     'callable': f,
955                     'introspectables': (),
956                     'kw': {},
957                     'discriminator': 1,
958                     'includepath': (),
959                     'order': 10,
960                 },
961                 {
962                     'info': 'sixth',
963                     'args': (1,),
964                     'callable': f,
965                     'introspectables': (),
966                     'kw': {},
967                     'discriminator': 2,
968                     'includepath': (),
969                     'order': 10,
970                 },
971                 {
972                     'info': 'seventh',
973                     'args': (1,),
974                     'callable': f,
975                     'introspectables': (),
976                     'kw': {},
977                     'discriminator': 3,
978                     'includepath': (),
979                     'order': 10,
980                 },
981                 {
982                     'info': 'eighth',
983                     'args': (4,),
984                     'callable': f,
985                     'introspectables': (),
986                     'kw': {},
987                     'discriminator': 5,
988                     'includepath': ('y',),
989                     'order': 99999,
990                 },
991             ],
992         )
993
994     def test_override_success_across_orders(self):
995         from . import dummyfactory as f
996
997         result = self._callFUT(
998             [
999                 (1, f, (2,), {}, ('x',), 'eek', 0),
1000                 (1, f, (3,), {}, ('x', 'y'), 'ack', 10),
1001             ]
1002         )
1003         result = list(result)
1004         self.assertEqual(
1005             result,
1006             [
1007                 {
1008                     'info': 'eek',
1009                     'args': (2,),
1010                     'callable': f,
1011                     'introspectables': (),
1012                     'kw': {},
1013                     'discriminator': 1,
1014                     'includepath': ('x',),
1015                     'order': 0,
1016                 }
1017             ],
1018         )
1019
1020     def test_conflicts_across_orders(self):
1021         from . import dummyfactory as f
1022
1023         result = self._callFUT(
1024             [
1025                 (1, f, (2,), {}, ('x', 'y'), 'eek', 0),
1026                 (1, f, (3,), {}, ('x'), 'ack', 10),
1027             ]
1028         )
1029         self.assertRaises(ConfigurationConflictError, list, result)
1030
1031
d579f2 1032 class TestActionInfo(unittest.TestCase):
MM 1033     def _getTargetClass(self):
1034         from pyramid.config.actions import ActionInfo
1035
1036         return ActionInfo
1037
1038     def _makeOne(self, filename, lineno, function, linerepr):
1039         return self._getTargetClass()(filename, lineno, function, linerepr)
1040
1041     def test_class_conforms(self):
1042         from zope.interface.verify import verifyClass
1043         from pyramid.interfaces import IActionInfo
1044
1045         verifyClass(IActionInfo, self._getTargetClass())
1046
1047     def test_instance_conforms(self):
1048         from zope.interface.verify import verifyObject
1049         from pyramid.interfaces import IActionInfo
1050
1051         verifyObject(IActionInfo, self._makeOne('f', 0, 'f', 'f'))
1052
1053     def test_ctor(self):
1054         inst = self._makeOne('filename', 10, 'function', 'src')
1055         self.assertEqual(inst.file, 'filename')
1056         self.assertEqual(inst.line, 10)
1057         self.assertEqual(inst.function, 'function')
1058         self.assertEqual(inst.src, 'src')
1059
1060     def test___str__(self):
1061         inst = self._makeOne('filename', 0, 'function', '   linerepr  ')
1062         self.assertEqual(
1063             str(inst), "Line 0 of file filename:\n       linerepr  "
1064         )
1065
1066
e4c057 1067 def _conflictFunctions(e):
MM 1068     conflicts = e._conflicts.values()
1069     for conflict in conflicts:
1070         for confinst in conflict:
1071             yield confinst.function
1072
1073
1074 class DummyActionState(object):
1075     autocommit = False
1076     info = ''
1077
1078     def __init__(self):
1079         self.actions = []
1080
1081     def action(self, *arg, **kw):
1082         self.actions.append((arg, kw))
1083
1084
1085 class DummyIntrospectable(object):
1086     def __init__(self):
1087         self.registered = []
1088
1089     def register(self, introspector, action_info):
1090         self.registered.append((introspector, action_info))