Repository: getnikola/nikola

Issue:
Custom post scanners are activated randomly
```
% nikola build
Scanning posts.......Scanning package index posts...done!
% nikola build
Scanning package index posts...Scanning posts.......done!
```

Custom post scanners are activated randomly
```
% nikola build
Scanning posts.......Scanning package index posts...done!
% nikola build
Scanning package index posts...Scanning posts.......done!
```



Discussion:
I guess that's for the same reason that all kind of plugins are executed in a random order: because they were at some point collected in a `dict`. One possibility would be to order them by name. But then one can also ask whether we want to do the same thing when generating tasks for plugins as well. On the other hand, having this randomization also helps finding bugs :)

(That’s actually true: one random ordering meant my task plugin crashed, because I forgot about `scan_posts()`)

So, not a bug? In any case, dicts are becoming ordered in python 3.6 or something.

@ralsina For a few releases, fixed order in dicts will only be a side-effect not to be relied upon: https://docs.python.org/3.6/whatsnew/3.6.html#other-language-changes

I don't agree with the 'minor'  and 'visual' labels ;-)

My plugin to solve #2625 crashes in 50% of the cases, because it relies on sites.posts_per_classification.
But as this information is generated by the taxonomies_classifier plugin, it is missing if my plugin is executed first.

Ideally ordering would be done by specifing that my plugin depends on taxonomies_classifier, but alphabetic ordering or manually specifing the order in the config would also do as a workaround.

If this is not solved, my plugin would have to replicate code for classifying posts into sections, increasing the code surface for bugs.
If your plugin crashes in 50% of the cases, then it's a good sign there's something wrong with your plugin ;) If your plugin needs the classified taxonomies and not only the scanned posts, you should listen for the `taxonomies_classified` signal and not for the `scanned` signal. Then it is guaranteed that all classifications are done. (Sorry that I missed that when looking at your code.)
I guess that's for the same reason that all kind of plugins are executed in a random order: because they were at some point collected in a `dict`. One possibility would be to order them by name. But then one can also ask whether we want to do the same thing when generating tasks for plugins as well. On the other hand, having this randomization also helps finding bugs :)

(That’s actually true: one random ordering meant my task plugin crashed, because I forgot about `scan_posts()`)

So, not a bug? In any case, dicts are becoming ordered in python 3.6 or something.

@ralsina For a few releases, fixed order in dicts will only be a side-effect not to be relied upon: https://docs.python.org/3.6/whatsnew/3.6.html#other-language-changes

I don't agree with the 'minor'  and 'visual' labels ;-)

My plugin to solve #2625 crashes in 50% of the cases, because it relies on sites.posts_per_classification.
But as this information is generated by the taxonomies_classifier plugin, it is missing if my plugin is executed first.

Ideally ordering would be done by specifing that my plugin depends on taxonomies_classifier, but alphabetic ordering or manually specifing the order in the config would also do as a workaround.

If this is not solved, my plugin would have to replicate code for classifying posts into sections, increasing the code surface for bugs.
If your plugin crashes in 50% of the cases, then it's a good sign there's something wrong with your plugin ;) If your plugin needs the classified taxonomies and not only the scanned posts, you should listen for the `taxonomies_classified` signal and not for the `scanned` signal. Then it is guaranteed that all classifications are done. (Sorry that I missed that when looking at your code.)

Resolving patch:
diff --git a/nikola/nikola.py b/nikola/nikola.py
--- a/nikola/nikola.py
+++ b/nikola/nikola.py
@@ -34,6 +34,7 @@
 import datetime
 import functools
 import locale
+import operator
 import os
 import json
 import sys
@@ -2098,7 +2099,7 @@ def scan_posts(self, really=False, ignore_quit=False, quiet=False):
         self.timeline = []
         self.pages = []
 
-        for p in self.plugin_manager.getPluginsOfCategory('PostScanner'):
+        for p in sorted(self.plugin_manager.getPluginsOfCategory('PostScanner'), key=operator.attrgetter('name')):
             timeline = p.plugin_object.scan()
             # FIXME: can there be conflicts here?
             self.timeline.extend(timeline)


Test patch: