Repository: getpelican/pelican

Issue:
Conditionally abort on conflicting article paths?
Right now it seems that if I accidentally define the same slug, one of the articles arbitrarily wins and may (depending on my ARTICLE_\* settings) overwrite some other article.  Ideally I would like the ability to see an error in that case as a safety check to avoid accidentally blowing away content when I publish.  

Currently there's a warning for overlapping slugs in utils.process_translations, but I would like the ability for an error and non-zero exit status when overwriting other content to handle aborting in an automated publish script more easily.



Discussion:
Yep, this makes sense. Including for 3.1 release.


Resolving patch:
diff --git a/pelican/utils.py b/pelican/utils.py
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -418,15 +418,22 @@ def process_translations(content_list):
         if not default_lang_items:
             default_lang_items = items
 
+        # display warnings if several items have the same lang
+        for lang, lang_items in groupby(items, attrgetter('lang')):
+            lang_items = list(lang_items)
+            len_ = len(lang_items)
+            if len_ > 1:
+                logger.warning('There are %s variants of "%s" with lang %s' \
+                        % (len_, slug, lang))
+                for x in lang_items:
+                    logger.warning('    %s' % x.source_path)
+
         # find items with default language
         default_lang_items = list(filter(attrgetter('in_default_lang'),
                 default_lang_items))
-        len_ = len(default_lang_items)
-        if len_ > 1:
-            logger.warning('there are %s variants of "%s"' % (len_, slug))
-            for x in default_lang_items:
-                logger.warning('    {}'.format(x.source_path))
-        elif len_ == 0:
+
+        # if there is no article with default language, take an other one
+        if not default_lang_items:
             default_lang_items = items[:1]
 
         if not slug:
diff --git a/pelican/writers.py b/pelican/writers.py
--- a/pelican/writers.py
+++ b/pelican/writers.py
@@ -21,6 +21,7 @@ def __init__(self, output_path, settings=None):
         self.output_path = output_path
         self.reminder = dict()
         self.settings = settings or {}
+        self._written_files = set()
 
     def _create_new_feed(self, feed_type, context):
         feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed
@@ -46,6 +47,16 @@ def _add_item_to_the_feed(self, feed, item):
             pubdate=set_date_tzinfo(item.date,
                 self.settings.get('TIMEZONE', None)))
 
+    def _open_w(self, filename, encoding):
+        """Open a file to write some content to it.
+
+        Exit if we have already written to that file.
+        """
+        if filename in self._written_files:
+            raise IOError('File %s is to be overwritten' % filename)
+        self._written_files.add(filename)
+        return open(filename, 'w', encoding=encoding)
+
     def write_feed(self, elements, context, path=None, feed_type='atom'):
         """Generate a feed with the list of articles provided
 
@@ -82,7 +93,7 @@ def write_feed(self, elements, context, path=None, feed_type='atom'):
                     pass
 
                 encoding = 'utf-8' if six.PY3 else None
-                with open(complete_path, 'w', encoding=encoding) as fp:
+                with self._open_w(complete_path, encoding) as fp:
                     feed.write(fp, 'utf-8')
                     logger.info('writing %s' % complete_path)
             return feed
@@ -121,7 +132,7 @@ def _write_file(template, localcontext, output_path, name):
                 os.makedirs(os.path.dirname(path))
             except Exception:
                 pass
-            with open(path, 'w', encoding='utf-8') as f:
+            with self._open_w(path, 'utf-8') as f:
                 f.write(output)
             logger.info('writing {}'.format(path))
 


Test patch: