]> git.ipfire.org Git - collecty.git/commitdiff
Migrate to Python 3
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 27 May 2015 15:46:20 +0000 (15:46 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 27 May 2015 15:46:20 +0000 (15:46 +0000)
23 files changed:
configure.ac
src/_collectymodule.c
src/collecty/__init__.py
src/collecty/bus.py
src/collecty/client.py
src/collecty/constants.py
src/collecty/daemon.py
src/collecty/errors.py
src/collecty/i18n.py
src/collecty/logger.py
src/collecty/ping.py
src/collecty/plugins/__init__.py
src/collecty/plugins/base.py
src/collecty/plugins/conntrack.py
src/collecty/plugins/cpu.py
src/collecty/plugins/cpufreq.py
src/collecty/plugins/disk.py
src/collecty/plugins/entropy.py
src/collecty/plugins/interface.py
src/collecty/plugins/latency.py
src/collecty/plugins/loadavg.py
src/collecty/plugins/memory.py
src/collecty/plugins/sensors.py

index ab7302764105c5a1a41c5cc469f2eeb9373c9566..46fc55bd2fd6592c3fafadf65c13af8a54c5f224 100644 (file)
@@ -62,7 +62,7 @@ AC_PROG_GCC_TRADITIONAL
 AC_PATH_PROG([XSLTPROC], [xsltproc])
 
 # Python
-AM_PATH_PYTHON([2.7])
+AM_PATH_PYTHON([3.2])
 PKG_CHECK_MODULES([PYTHON], [python-${PYTHON_VERSION}])
 
 # libatasmart
index e57970eabf904e0d5380de7d56b17e075acb8628..422c27dd538e60a86394ed41f37515922aba101c 100644 (file)
@@ -45,7 +45,7 @@ static void BlockDevice_dealloc(BlockDevice* self) {
        if (self->path)
                free(self->path);
 
-       self->ob_type->tp_free((PyObject*)self);
+       Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static int BlockDevice_get_identity(BlockDevice* device) {
@@ -143,7 +143,7 @@ static int BlockDevice_init(BlockDevice* self, PyObject* args, PyObject* kwds) {
 static PyObject* BlockDevice_get_path(PyObject* self) {
        BlockDevice* device = (BlockDevice*)self;
 
-       return PyString_FromString(device->path);
+       return PyUnicode_FromString(device->path);
 }
 
 static void clean_string(char *s) {
@@ -195,7 +195,7 @@ static PyObject* BlockDevice_get_model(PyObject* self) {
        char model[MODEL_SIZE + 1];
        copy_string(model, device->identity.model, sizeof(model));
 
-       return PyString_FromString(model);
+       return PyUnicode_FromString(model);
 }
 
 static PyObject* BlockDevice_get_serial(PyObject* self) {
@@ -204,7 +204,7 @@ static PyObject* BlockDevice_get_serial(PyObject* self) {
        char serial[SERIAL_SIZE + 1];
        copy_string(serial, device->identity.serial_no, sizeof(serial));
 
-       return PyString_FromString(serial);
+       return PyUnicode_FromString(serial);
 }
 
 static PyObject* BlockDevice_is_smart_supported(PyObject* self) {
@@ -273,8 +273,7 @@ static PyMethodDef BlockDevice_methods[] = {
 };
 
 static PyTypeObject BlockDeviceType = {
-       PyObject_HEAD_INIT(NULL)
-       0,                                  /*ob_size*/
+       PyVarObject_HEAD_INIT(NULL, 0)
        "_collecty.BlockDevice",            /*tp_name*/
        sizeof(BlockDevice),                /*tp_basicsize*/
        0,                                  /*tp_itemsize*/
@@ -321,7 +320,7 @@ typedef struct {
 } SensorObject;
 
 static void Sensor_dealloc(SensorObject* self) {
-       self->ob_type->tp_free((PyObject*)self);
+       Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static PyObject* Sensor_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
@@ -338,7 +337,7 @@ static PyObject* Sensor_get_label(SensorObject* self) {
        char* label = sensors_get_label(self->chip, self->feature);
 
        if (label) {
-               PyObject* string = PyString_FromString(label);
+               PyObject* string = PyUnicode_FromString(label);
                free(label);
 
                return string;
@@ -356,7 +355,7 @@ static PyObject* Sensor_get_name(SensorObject* self) {
                return NULL;
        }
 
-       return PyString_FromString(chip_name);
+       return PyUnicode_FromString(chip_name);
 }
 
 static PyObject* Sensor_get_type(SensorObject* self) {
@@ -384,7 +383,7 @@ static PyObject* Sensor_get_type(SensorObject* self) {
        }
 
        if (type)
-               return PyString_FromString(type);
+               return PyUnicode_FromString(type);
 
        Py_RETURN_NONE;
 }
@@ -426,7 +425,7 @@ static PyObject* Sensor_get_bus(SensorObject* self) {
        }
 
        if (type)
-               return PyString_FromString(type);
+               return PyUnicode_FromString(type);
 
        Py_RETURN_NONE;
 }
@@ -602,7 +601,6 @@ static PyGetSetDef Sensor_getsetters[] = {
 
 static PyTypeObject SensorType = {
        PyObject_HEAD_INIT(NULL)
-       0,                                  /*ob_size*/
        "_collecty.Sensor",                 /*tp_name*/
        sizeof(SensorObject),               /*tp_basicsize*/
        0,                                  /*tp_itemsize*/
@@ -729,15 +727,32 @@ static PyMethodDef collecty_module_methods[] = {
        {NULL},
 };
 
-void init_collecty(void) {
+static struct PyModuleDef collecty_module = {
+       PyModuleDef_HEAD_INIT,
+       "_collecty",                        /* m_name */
+       "_collecty module",                 /* m_doc */
+       -1,                                 /* m_size */
+       collecty_module_methods,            /* m_methods */
+       NULL,                               /* m_reload */
+       NULL,                               /* m_traverse */
+       NULL,                               /* m_clear */
+       NULL,                               /* m_free */
+};
+
+PyMODINIT_FUNC PyInit__collecty(void) {
        if (PyType_Ready(&BlockDeviceType) < 0)
-               return;
+               return NULL;
 
        if (PyType_Ready(&SensorType) < 0)
-               return;
+               return NULL;
 
-       PyObject* m = Py_InitModule("_collecty", collecty_module_methods);
+       PyObject* m = PyModule_Create(&collecty_module);
 
+       Py_INCREF(&BlockDeviceType);
        PyModule_AddObject(m, "BlockDevice", (PyObject*)&BlockDeviceType);
+
+       Py_INCREF(&SensorType);
        PyModule_AddObject(m, "Sensor", (PyObject*)&SensorType);
+
+       return m;
 }
index ab16c1280fb92cbae5183db8a626673700a7c858..a1a60f010a65a9940d1824cd8140e3ccb4f474ea 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -20,7 +20,7 @@
 ###############################################################################
 
 # Initialize logging.
-import logger
+from . import logger
 
-from client import CollectyClient
-from daemon import Collecty
+from .client import CollectyClient
+from .daemon import Collecty
index 6a3f0bd1b3b0ae907190aee84122f4a3b1d318e6..d67332f082742f3d2a58b31b5b67c403a944171f 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
 import dbus
 import dbus.mainloop.glib
 import dbus.service
-import gobject
+import gi.repository.GLib
+import gi.repository.GObject
 import threading
 
-from constants import *
-from i18n import _
+from .constants import *
+from .i18n import _
 
 import logging
 log = logging.getLogger("collecty.bus")
 log.propagate = 1
 
-# Initialise the glib main loop
-dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-dbus.mainloop.glib.threads_init()
-
 class Bus(threading.Thread):
        def __init__(self, collecty):
                threading.Thread.__init__(self)
@@ -44,8 +41,11 @@ class Bus(threading.Thread):
                self.collecty = collecty
 
                # Initialise the main loop
-               gobject.threads_init()
-               self.loop = gobject.MainLoop()
+               gi.repository.GObject.threads_init()
+               dbus.mainloop.glib.threads_init()
+               dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+               self.loop = gi.repository.GLib.MainLoop()
 
                # Register the GraphGenerator interface
                self.generator = GraphGenerator(self.collecty)
@@ -54,7 +54,12 @@ class Bus(threading.Thread):
                log.debug(_("Bus thread has started"))
 
                # Run the main loop
-               self.loop.run()
+               try:
+                       self.loop.run()
+               except KeyboardInterrupt:
+                       self.collecty.shutdown()
+
+               log.debug(_("Bus thread has ended"))
 
        def shutdown(self):
                log.debug(_("Stopping bus thread"))
index 1f072256b3afb8cb9eb4379afe010e8c6f65ae13..0e870a2b0894881771f587280734fd5b08273a53 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -23,8 +23,8 @@ import argparse
 import dbus
 import sys
 
-from constants import *
-from i18n import _
+from .constants import *
+from .i18n import _
 
 import logging
 log = logging.getLogger("collectly.client")
@@ -44,7 +44,7 @@ class CollectyClient(object):
                templates = self.list_templates()
 
                for t in sorted(templates):
-                       print t
+                       print(t)
 
        def generate_graph(self, template_name, **kwargs):
                byte_array = self.proxy.GenerateGraph(template_name, kwargs,
index 8580d144780b3cf00c59250d061c72ec108baf5c..34d5406bf69566d0e1a0b19008f49175abab7b85 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -19,7 +19,7 @@
 #                                                                             #
 ###############################################################################
 
-from i18n import _
+from .i18n import _
 
 DATABASE_DIR = "/var/lib/collecty"
 
index bb46971493368d3d18789d118555870ac14c6268..167d000ee46e5399ea5111d63d21c9f27cb35617 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
 #                                                                             #
 ###############################################################################
 
-import Queue as queue
 import datetime
 import multiprocessing
+import queue
 import rrdtool
 import signal
 import threading
 import time
 
-import bus
-import plugins
+from . import bus
+from . import plugins
 
-from constants import *
-from i18n import _
+from .constants import *
+from .i18n import _
 
 import logging
 log = logging.getLogger("collecty")
@@ -96,6 +96,11 @@ class Collecty(object):
                # Register signal handlers.
                self.register_signal_handler()
 
+               # Cannot do anything if no plugins have been initialised
+               if not self.plugins:
+                       log.critical(_("No plugins have been initialised"))
+                       return
+
                # Start the bus
                self.bus.start()
 
@@ -335,7 +340,7 @@ class WriteQueue(threading.Thread):
                                results[result.file] = [result]
 
                # Write the collected data to disk
-               for filename, results in results.items():
+               for filename, results in list(results.items()):
                        self._commit_file(filename, results)
 
                duration = time.time() - time_start
@@ -367,8 +372,8 @@ class QueueObject(object):
        def __str__(self):
                return "%s:%s" % (self.time.strftime("%s"), self.data)
 
-       def __cmp__(self, other):
-               return cmp(self.time, other.time)
+       def __lt__(self, other):
+               return self.time < other.time
 
 
 class PluginTimer(object):
@@ -380,8 +385,8 @@ class PluginTimer(object):
        def __repr__(self):
                return "<%s %s>" % (self.__class__.__name__, self.deadline)
 
-       def __cmp__(self, other):
-               return cmp(self.deadline, other.deadline)
+       def __lt__(self, other):
+               return self.deadline < other.deadline
 
        def reset_deadline(self):
                self.deadline = datetime.datetime.utcnow() \
index 0dbe4851c6bd9aecbe074c61e596023542e7f0c2..dacacb18b02dd629958ad5c292add7f1b45432c9 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
index 75c78fe86f4a6f9da05c0d1b3e0301217c4c9201..a7c37015295550fb9afee67ff36bfd76bf2e636a 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
index f1c732ca017d3b00b6ec3a1130132c756f981475..e23af1936ad619def253ba194129b3f9fdc70df6 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
index 60b5537f3ead4b026b216267839c4931aa792f92..e2d797054b53e1906028f017edf87eacab49426b 100644 (file)
@@ -1,6 +1,4 @@
-#!/usr/bin/python
-
-from __future__ import division
+#!/usr/bin/python3
 
 import array
 import math
@@ -118,8 +116,8 @@ class Ping(object):
                # Send the packet.
                try:
                        s.sendto(packet, (self.destination, 0))
-               except socket.error, (errno, msg):
-                       if errno == 1: # Operation not permitted
+               except socket.error as e:
+                       if e.errno == 1: # Operation not permitted
                                # The packet could not be sent, probably because of
                                # wrong firewall settings.
                                return
@@ -175,7 +173,7 @@ class Ping(object):
                        Unpack tghe raw received IP and ICMP header informations to a dict
                """
                unpacked_data = struct.unpack(struct_format, data)
-               return dict(zip(names, unpacked_data))
+               return dict(list(zip(names, unpacked_data)))
 
        def _calculate_checksum(self, source_string):
                if len(source_string) % 2:
@@ -321,6 +319,6 @@ if __name__ == "__main__":
        p = Ping("ping.ipfire.org")
        p.run(count=5)
 
-       print "Min/Avg/Max/Stddev: %.2f/%.2f/%.2f/%.2f" % \
-               (p.min_time, p.avg_time, p.max_time, p.stddev)
-       print "Sent/Recv/Loss: %d/%d/%.2f" % (p.send_count, p.receive_count, p.loss)
+       print("Min/Avg/Max/Stddev: %.2f/%.2f/%.2f/%.2f" % \
+               (p.min_time, p.avg_time, p.max_time, p.stddev))
+       print("Sent/Recv/Loss: %d/%d/%.2f" % (p.send_count, p.receive_count, p.loss))
index b68acc93ef8551f23cb45e4a8d66028f3d4b82f3..3e92bf615f506e26ad683286a864333d24d33917 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
 #                                                                             #
 ###############################################################################
 
-from base import Timer, get
+from .base import Timer, get
 
-import base
-import conntrack
-import cpu
-import cpufreq
-import disk
-import entropy
-import interface
-import latency
-import loadavg
-import memory
-import sensors
+from . import base
+from . import conntrack
+from . import cpu
+from . import cpufreq
+from . import disk
+from . import entropy
+from . import interface
+from . import latency
+from . import loadavg
+from . import memory
+from . import sensors
index 8d768ae8b7e97b86409e32e811aed9c4fd5c4f4f..048ebcb9804a1787f78dc91852243eb5d4e84f68 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -19,8 +19,6 @@
 #                                                                             #
 ###############################################################################
 
-from __future__ import division
-
 import datetime
 import logging
 import math
@@ -29,18 +27,11 @@ import rrdtool
 import tempfile
 import threading
 import time
+import unicodedata
 
 from ..constants import *
 from ..i18n import _
 
-_plugins = {}
-
-def get():
-       """
-               Returns a list with all automatically registered plugins.
-       """
-       return _plugins.values()
-
 class Timer(object):
        def __init__(self, timeout, heartbeat=1):
                self.timeout = timeout
@@ -73,7 +64,30 @@ class Timer(object):
                return self.elapsed > self.timeout
 
 
-class Plugin(object):
+class PluginRegistration(type):
+       plugins = {}
+
+       def __init__(plugin, name, bases, dict):
+               type.__init__(plugin, name, bases, dict)
+
+               # The main class from which is inherited is not registered
+               # as a plugin.
+               if name == "Plugin":
+                       return
+
+               if not all((plugin.name, plugin.description)):
+                       raise RuntimeError(_("Plugin is not properly configured: %s") % plugin)
+
+               PluginRegistration.plugins[plugin.name] = plugin
+
+
+def get():
+       """
+               Returns a list with all automatically registered plugins.
+       """
+       return PluginRegistration.plugins.values()
+
+class Plugin(object, metaclass=PluginRegistration):
        # The name of this plugin.
        name = None
 
@@ -87,22 +101,6 @@ class Plugin(object):
        # The default interval for all plugins
        interval = 60
 
-       # Automatically register all providers.
-       class __metaclass__(type):
-               def __init__(plugin, name, bases, dict):
-                       type.__init__(plugin, name, bases, dict)
-
-                       # The main class from which is inherited is not registered
-                       # as a plugin.
-                       if name == "Plugin":
-                               return
-
-                       if not all((plugin.name, plugin.description)):
-                               raise RuntimeError(_("Plugin is not properly configured: %s") \
-                                       % plugin)
-
-                       _plugins[plugin.name] = plugin
-
        def __init__(self, collecty, **kwargs):
                self.collecty = collecty
 
@@ -254,7 +252,7 @@ class Object(object):
        @staticmethod
        def _normalise_filename(filename):
                # Convert the filename into ASCII characters only
-               filename = filename.encode("ascii", "ignore")
+               filename = unicodedata.normalize("NFKC", filename)
 
                # Replace any spaces by dashes
                filename = filename.replace(" ", "-")
index 0e1c6d9009c72fff278cd410325cb66ccaa73f4e..82f9e6afb61ebd5c8d8da712bb4181f1d6478b5b 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -21,7 +21,7 @@
 
 import os
 
-import base
+from . import base
 
 from ..i18n import _
 
@@ -117,7 +117,7 @@ class ConntrackTable(object):
                                        layer4_protocol = "other"
 
                                # Count connection states
-                               if self.protocol_states.has_key(layer4_protocol):
+                               if layer4_protocol in self.protocol_states:
                                        state = line[5]
 
                                        try:
index 962163f388ef93537abee5fe9c2da581d62b00ba..09d5051c1542b78edc29869fdd1ee194b764285d 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -19,9 +19,9 @@
 #                                                                             #
 ###############################################################################
 
-from __future__ import division
 
-import base
+
+from . import base
 
 from ..i18n import _
 
index 7deeae677215538821800157156352dcc176fbe5..19f099a31981ae87d2b78d2bc83aea4db0129e0f 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -22,7 +22,7 @@
 import os
 import re
 
-import base
+from . import base
 
 from ..i18n import _
 
index b0f40a2924c4939b53f284034b5d844f53377427..f27408590d7f98298bbe1b25a975b17589726237 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -23,7 +23,7 @@ from collecty import _collecty
 import os
 import re
 
-import base
+from . import base
 
 from ..i18n import _
 
index e6e920d02ce528ff309d1c1efb46490dedbe4bce..0b3fa5af591abc8b7b14c17a30808f48b69a8aca 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -21,7 +21,7 @@
 
 import os
 
-import base
+from . import base
 
 from ..i18n import _
 
index addca198512d6aad4bcc0e6924b125321802df37..eeb8be6737d792ca77f6ca726813f6684759836c 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
 #                                                                             #
 ###############################################################################
 
-from __future__ import division
+
 
 import os
 
-import base
+from . import base
 
 from ..i18n import _
 
index 0b2324a75b5f28a8fe0817c8b3b2d8dbf89a38ef..007262cfdc8d243cce5205d4bf2439ef28da539f 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -21,7 +21,7 @@
 
 import collecty.ping
 
-import base
+from . import base
 
 from ..i18n import _
 
@@ -107,7 +107,7 @@ class LatencyObject(base.Object):
                        ping = collecty.ping.Ping(destination=self.hostname, timeout=20000)
                        ping.run(count=5, deadline=self.deadline)
 
-               except collecty.ping.PingError, e:
+               except collecty.ping.PingError as e:
                        self.log.warning(_("Could not run latency check for %(host)s: %(msg)s") \
                                % { "host" : self.hostname, "msg" : e.msg })
                        return
index 24cce740ebf334c51248edff858e6535d6bbf3bb..090c9f42ad60be33f331481a941b6aabd400b856 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -21,7 +21,7 @@
 
 import os
 
-import base
+from . import base
 
 from ..i18n import _
 
index 55d6586493ca3943f80d72fecec2ce6c11422206..3310f718e84fc4c79a3a705710cecd5c7f540e6d 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 ###############################################################################
 #                                                                             #
 # collecty - A system statistics collection daemon for IPFire                 #
@@ -19,9 +19,9 @@
 #                                                                             #
 ###############################################################################
 
-from __future__ import division
 
-import base
+
+from . import base
 
 from ..i18n import _
 
index e4c247ea35fc5e006d18a2ec0d92e8357a47a0f3..f3b3c256cf9524014ac9092518fcce00c1f27277 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 # encoding: utf-8
 ###############################################################################
 #                                                                             #
@@ -24,7 +24,7 @@ from collecty import _collecty
 import os
 import re
 
-import base
+from . import base
 
 from ..i18n import _