Repository: pibooth/pibooth

Issue:
EOFError: Ran out of input ??
there was no more space on the disk, then I got this error
pibooth does not start anymore

pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
pygame-menu 4.0.7
[ INFO    ] pibooth           : Installed plugins: extra-lights-1.0.1, picture-template-1.1.0, sound-effects-0.0.3, qrcode-1.0.2, dropbox-0.0.3
[ INFO    ] pibooth           : Starting the photo booth application on Raspberry pi 4B
Traceback (most recent call last):
  File "/home/pi/.local/bin/pibooth", line 8, in <module>
    sys.exit(main())
  File "/home/pi/.local/lib/python3.9/site-packages/pibooth/booth.py", line 490, in main
    app = PiApplication(config, plugin_manager)
  File "/home/pi/.local/lib/python3.9/site-packages/pibooth/booth.py", line 127, in __init__
    self.count = Counters(self._config.join_path("counters.pickle"),
  File "/home/pi/.local/lib/python3.9/site-packages/pibooth/counters.py", line 14, in __init__
    self.load()
  File "/home/pi/.local/lib/python3.9/site-packages/pibooth/counters.py", line 54, in load
    self.data.update(pickle.load(fp))
EOFError: Ran out of input




Discussion:
codecov-commenter: ## [Codecov](https://codecov.io/gh/pibooth/pibooth/pull/436?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#436](https://codecov.io/gh/pibooth/pibooth/pull/436?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (04e5965) into [3.x](https://codecov.io/gh/pibooth/pibooth/commit/03c904cc8a92f628df0e73034f0f674e7e21f7ee?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (03c904c) will **decrease** coverage by `0.03%`.
> The diff coverage is `31.57%`.

:mega: This organization is not using Codecov’s [GitHub App Integration](https://github.com/apps/codecov). We recommend you install it so Codecov can continue to function properly for your repositories. [Learn more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)

```diff
@@            Coverage Diff             @@
##              3.x     #436      +/-   ##
==========================================
- Coverage   43.36%   43.34%   -0.03%     
==========================================
  Files          51       51              
  Lines        4084     4086       +2     
==========================================
  Hits         1771     1771              
- Misses       2313     2315       +2     
```


| [Impacted Files](https://codecov.io/gh/pibooth/pibooth/pull/436?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [pibooth/app.py](https://codecov.io/gh/pibooth/pibooth/pull/436?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGlib290aC9hcHAucHk=) | `0.00% <0.00%> (ø)` | |
| [pibooth/view/pygame/menu.py](https://codecov.io/gh/pibooth/pibooth/pull/436?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGlib290aC92aWV3L3B5Z2FtZS9tZW51LnB5) | `28.83% <0.00%> (-0.14%)` | :arrow_down: |
| [pibooth/counters.py](https://codecov.io/gh/pibooth/pibooth/pull/436?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGlib290aC9jb3VudGVycy5weQ==) | `87.50% <50.00%> (-6.79%)` | :arrow_down: |
| [pibooth/view/pygame/sprites.py](https://codecov.io/gh/pibooth/pibooth/pull/436?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-cGlib290aC92aWV3L3B5Z2FtZS9zcHJpdGVzLnB5) | `76.36% <60.00%> (-0.14%)` | :arrow_down: |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)


Patch:
diff --git a/.pylintrc b/.pylintrc
index cb2c887..bf04d0f 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -48,7 +48,7 @@ ignore=CVS
 # ignore-list. The regex matches against paths and can be in Posix or Windows
 # format. Because '\' represents the directory delimiter on Windows systems, it
 # can't be used as an escape character.
-ignore-paths=
+ignore-paths=docs/conf.py
 
 # Files or directories matching the regular expression patterns are skipped.
 # The regex matches against base names, not paths. The default value ignores
diff --git a/pibooth/app.py b/pibooth/app.py
index 46c24eb..65ee132 100644
--- a/pibooth/app.py
+++ b/pibooth/app.py
@@ -139,7 +139,7 @@ class PiboothApplication(object):
         Only parameters that can be changed at runtime are restored.
         """
         # Handle the language configuration
-        language.CURRENT = self._config.get('GENERAL', 'language')
+        language.set_current(self._config.get('GENERAL', 'language'))
         if self._config.get('WINDOW', 'font').endswith('.ttf') or self._config.get('WINDOW', 'font').endswith('.otf'):
             fonts.CURRENT = fonts.get_filename(self._config.getpath('WINDOW', 'font'))
         else:
@@ -223,17 +223,16 @@ class PiboothApplication(object):
             raise EnvironmentError("The 'capture_date' attribute is not set yet")
         return f"{self.capture_date}_pibooth.jpg"
 
-    def enable_plugin(self, name):
+    def enable_plugin(self, plugin):
         """Enable plugin with given name. The "configure" and "startup" hooks will
         be called if never done before.
 
-        :param name: plugin name
-        :type name: str
+        :param plugin: plugin to disable
+        :type plugin: object
         """
-        plugin = self._pm.get_plugin(name)
         if not self._pm.is_registered(plugin):
             self._pm.register(plugin)
-            LOGGER.debug("Plugin '%s' enable", name)
+            LOGGER.debug("Plugin '%s' enable", self._pm.get_name(plugin))
             # Because no hook is called for plugins disabled at pibooth startup, need to
             # ensure that mandatory hooks have been called when enabling a plugin
             if 'pibooth_configure' not in self._pm.get_calls_history(plugin):
@@ -243,15 +242,14 @@ class PiboothApplication(object):
                 hook = self._pm.subset_hook_caller_for_plugin('pibooth_startup', plugin)
                 hook(cfg=self._config, app=self)
 
-    def disable_plugin(self, name):
+    def disable_plugin(self, plugin):
         """Disable plugin with given name.
 
-        :param name: plugin name
-        :type name: str
+        :param plugin: plugin to disable
+        :type plugin: object
         """
-        plugin = self._pm.get_plugin(name)
         if self._pm.is_registered(plugin):
-            LOGGER.debug("Plugin '%s' disabled", name)
+            LOGGER.debug("Plugin '%s' disabled", self._pm.get_name(plugin))
             self._pm.unregister(plugin)
 
     def update(self, events):
diff --git a/pibooth/counters.py b/pibooth/counters.py
index 1163c7c..a3e7a0f 100644
--- a/pibooth/counters.py
+++ b/pibooth/counters.py
@@ -2,6 +2,7 @@
 
 import pickle
 import os.path as osp
+from pibooth.utils import LOGGER
 
 
 class Counters(object):
@@ -51,7 +52,11 @@ class Counters(object):
         """Load the saved counters.
         """
         with open(self.filename, 'rb') as fp:
-            self.data.update(pickle.load(fp))
+            try:
+                self.data.update(pickle.load(fp))
+            except (pickle.UnpicklingError, EOFError):
+                LOGGER.warning("File '%s' corrupted, counters data are lost", self.filename)
+                self.save()
 
     def reset(self):
         """Reset all counters.
diff --git a/pibooth/language.py b/pibooth/language.py
index 8a03188..0c0a085 100644
--- a/pibooth/language.py
+++ b/pibooth/language.py
@@ -235,6 +235,17 @@ def edit():
     open_text_editor(PARSER.filename)
 
 
+def set_current(name):
+    """Set current language.
+
+    :param name: alpha-2 country name
+    :type name; str
+    """
+    global CURRENT
+    assert name in DEFAULT, f"Unknown '{name}' language (possible choice: {', '.join(get_supported_languages())})"
+    CURRENT = name
+
+
 def get_supported_languages():
     """Return the list of supported language.
     """
diff --git a/pibooth/view/pygame/menu.py b/pibooth/view/pygame/menu.py
index 7cea714..e2e85d0 100644
--- a/pibooth/view/pygame/menu.py
+++ b/pibooth/view/pygame/menu.py
@@ -210,7 +210,7 @@ class PygameMenu(object):
                                    # Parameters passed to callback:
                                    section='GENERAL',
                                    option='plugins_disabled',
-                                   plugin_name=self.pm.get_name(plugin))
+                                   plugin=plugin)
         menu.enable_render()
         return menu
 
@@ -245,14 +245,15 @@ class PygameMenu(object):
     def on_plugin_toggled(self, activated, **kwargs):
         """Called when a plugin active state is toggled.
         """
-        plugin_name = kwargs['plugin_name']
+        plugin = kwargs['plugin']
+        plugin_name = self.pm.get_name(plugin)
         disabled = self.cfg.gettuple(kwargs['section'], kwargs['option'], str)
         if activated:
-            self.app.enable_plugin(plugin_name)
+            self.app.enable_plugin(plugin)
             disabled = tuple([name for name in disabled if plugin_name != name])
             self._changed = True
         elif not activated:
-            self.app.disable_plugin(plugin_name)
+            self.app.disable_plugin(plugin)
             if plugin_name not in disabled:
                 disabled += (plugin_name,)
                 self._changed = True
diff --git a/pibooth/view/pygame/sprites.py b/pibooth/view/pygame/sprites.py
index 9d44fc0..8d19eb1 100644
--- a/pibooth/view/pygame/sprites.py
+++ b/pibooth/view/pygame/sprites.py
@@ -6,7 +6,7 @@ import pygame
 
 from pibooth import pictures
 from pibooth import evts
-from pibooth.utils import PollingTimer
+from pibooth.utils import PollingTimer, LOGGER
 from pibooth.view.base import BaseScene
 
 
@@ -527,12 +527,11 @@ class BasePygameScene(BaseScene):
             layer = 2
         elif layer == 0 and isinstance(sprite, TextSprite):
             layer = 1
-        elif layer == 0:
-            count = len(self.sprites.get_sprites_from_layer(layer))
-            assert count == 0, f"Only one background sprite can be defined (current={count})"
-        elif layer == 3:
-            count = len(self.sprites.get_sprites_from_layer(layer))
-            assert count == 0, f"Only one main image sprite can be defined (current={count})"
+        elif layer == 0 or layer == 3:
+            for oldsprite in self.sprites.get_sprites_from_layer(layer):
+                LOGGER.debug("Remove %s sprite '%s'", "background" if layer == 0 else "main image", oldsprite)
+                self.sprites.remove(oldsprite)
+
         self.sprites.add(sprite, layer=layer)
         if outlines:
             self.sprites.add(OutlinesSprite(sprite), layer=5)


Test patch:
diff --git a/tests/test_language.py b/tests/test_language.py
new file mode 100644
index 0000000..4e65ced
--- /dev/null
+++ b/tests/test_language.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+
+import pytest
+from pibooth import language
+
+
+def test_supported_language(init):
+    assert 'en' in language.get_supported_languages()
+    assert 'fr' in language.get_supported_languages()
+
+
+def test_current(init):
+    with pytest.raises(AssertionError):
+        language.set_current('be')
+    language.set_current('fr')
+
+
+def test_translate(init):
+    language.set_current('en')
+    assert language.get_translated_text('finished') == "Thanks"
+    language.set_current('fr')
+    assert language.get_translated_text('finished') == "Merci"