Prefix for class attributes in DynamicModule and IntrospectionModule.



Both DynamicModule and IntrospectionModule work by implementing
__getattr__ so that if Python cannot find an attribute as an instance
of the class, the custom implementations will be able to wrap and load
the requested attribute from the typelib.

However, if the attribute requested already exists, Python will grab
it directly from the instance's __dict__ and not call __getattr__ at
all. You can see that third party developers can access all the class
attributes, for which __getattr__ will not be called:

>>> from gi.repository import Gtk
>>> Gtk._namespace
'Gtk'
>>> Gtk.version
'3.0'

The attributes '_namespace' and 'version' are not in the typelib, they
are class attributes from DynamicModule and IntrospectionModule and
respectively.

But what if the typelib defines an attribute with the same name as one
of the class attributes? Gstreamer does exactly that. It has a
*function* gst_version[1] which is Gst.version in the typelib. This
obviously will fail, because the class attribute (a string) is
returned instead of the wrapped function.

>>> from gi.repository import Gst
>>> Gst.version()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>>

The simplest way to fix this is to simply put a prefix on all class
members (variables and methods). I have included a patch using the
prefix '_pygi_', starting with a single underscore to avoid Python's
name mangling, since overrides still need access to these members
outside the class.

I am looking for feedback on this patch. It passes all tests, but is a
quick first attempt.

[1] http://www.gstreamer.net/data/doc/gstreamer/head/gstreamer/html/gstreamer-Gst.html#gst-version
diff --git a/gi/module.py b/gi/module.py
index f19dfb6..711f581 100644
--- a/gi/module.py
+++ b/gi/module.py
@@ -81,18 +81,18 @@ class IntrospectionModule(object):
 
     def __init__(self, namespace, version=None):
         repository.require(namespace, version)
-        self._namespace = namespace
-        self.version = version
+        self._pygi_namespace = namespace
+        self._pygi_version = version
         self.__name__ = 'gi.repository.' + namespace
 
-        repository.require(self._namespace, self.version)
-        self.__path__ = repository.get_typelib_path(self._namespace)
+        repository.require(self._pygi_namespace, self._pygi_version)
+        self.__path__ = repository.get_typelib_path(self._pygi_namespace)
 
-        if self.version is None:
-            self.version = repository.get_version(self._namespace)
+        if self._pygi_version is None:
+            self._pygi_version = repository.get_version(self._pygi_namespace)
 
     def __getattr__(self, name):
-        info = repository.find_by_name(self._namespace, name)
+        info = repository.find_by_name(self._pygi_namespace, name)
         if not info:
             raise AttributeError("%r object has no attribute %r" % (
                     self.__name__, name))
@@ -154,7 +154,7 @@ class IntrospectionModule(object):
             name = info.get_name()
             dict_ = {
                 '__info__': info,
-                '__module__': 'gi.repository.' + self._namespace,
+                '__module__': 'gi.repository.' + self._pygi_namespace,
                 '__gtype__': g_type
             }
             wrapper = metaclass(name, bases, dict_)
@@ -174,8 +174,8 @@ class IntrospectionModule(object):
         return wrapper
 
     def __repr__(self):
-        path = repository.get_typelib_path(self._namespace)
-        return "<IntrospectionModule %r from %r>" % (self._namespace, path)
+        path = repository.get_typelib_path(self._pygi_namespace)
+        return "<IntrospectionModule %r from %r>" % (self._pygi_namespace, path)
 
     def __dir__ (self):
         # Python's default dir() is just dir(self.__class__) + self.__dict__.keys()
@@ -184,7 +184,7 @@ class IntrospectionModule(object):
 
         # update *set* because some repository attributes have already been
         # wrapped by __getattr__() and included in self.__dict__
-        namespace_infos = repository.get_infos(self._namespace)
+        namespace_infos = repository.get_infos(self._pygi_namespace)
         result.update(info.get_name() for info in namespace_infos)
 
         return list(result)
@@ -222,64 +222,64 @@ class DynamicGObjectModule(IntrospectionModule):
 
 class DynamicModule(object):
     def __init__(self, namespace):
-        self._namespace = namespace
-        self.introspection_module = None
-        self._version = None
-        self._overrides_module = None
+        self._pygi_namespace = namespace
+        self._pygi_introspection_module = None
+        self._pygi_version = None
+        self._pygi_overrides_module = None
         self.__path__ = None
 
     def require_version(self, version):
-        if self.introspection_module is not None and \
-                self.introspection_module.version != version:
+        if self._pygi_introspection_module is not None and \
+                self._pygi_introspection_module._pygi_version != version:
             raise RuntimeError('Module has been already loaded ')
-        self._version = version
+        self._pygi_version = version
 
-    def _import(self):
-        self.introspection_module = IntrospectionModule(self._namespace,
-                                                        self._version)
+    def _pygi_import(self):
+        self._pygi_introspection_module = IntrospectionModule(self._pygi_namespace,
+                                                        self._pygi_version)
 
-        overrides_modules = __import__('gi.overrides', fromlist=[self._namespace])
-        self._overrides_module = getattr(overrides_modules, self._namespace, None)
-        self.__path__ = repository.get_typelib_path(self._namespace)
+        overrides_modules = __import__('gi.overrides', fromlist=[self._pygi_namespace])
+        self._pygi_overrides_module = getattr(overrides_modules, self._pygi_namespace, None)
+        self.__path__ = repository.get_typelib_path(self._pygi_namespace)
 
     def __getattr__(self, name):
-        if self.introspection_module is None:
-            self._import()
+        if self._pygi_introspection_module is None:
+            self._pygi_import()
 
-        if self._overrides_module is not None:
-            override_exports = getattr(self._overrides_module, '__all__', ())
+        if self._pygi_overrides_module is not None:
+            override_exports = getattr(self._pygi_overrides_module, '__all__', ())
             if name in override_exports:
-                return getattr(self._overrides_module, name, None)
+                return getattr(self._pygi_overrides_module, name, None)
         else:
             # check the registry just in case the module hasn't loaded yet
             # TODO: Only gtypes are registered in the registry right now 
             #       but it would be nice to register all overrides and 
             #       get rid of the module imports. We might actually see a 
             #       speedup.
-            key = '%s.%s' % (self._namespace, name)
+            key = '%s.%s' % (self._pygi_namespace, name)
             if key in registry:
                 return registry[key]
 
-        return getattr(self.introspection_module, name)
+        return getattr(self._pygi_introspection_module, name)
 
     def __dir__ (self):
-        if self.introspection_module is None:
-            self._import()
+        if self._pygi_introspection_module is None:
+            self._pygi_import()
             
         # Python's default dir() is just dir(self.__class__) + self.__dict__.keys()
         result = set(dir(self.__class__))
         result.update(self.__dict__.keys())
         
-        result.update(dir(self.introspection_module))
-        override_exports = getattr(self._overrides_module, '__all__', ())
+        result.update(dir(self._pygi_introspection_module))
+        override_exports = getattr(self._pygi_overrides_module, '__all__', ())
         result.update(override_exports)
         return list(result)
 
     def __repr__(self):
-        repository.require(self._namespace, self._version)
+        repository.require(self._pygi_namespace, self._pygi_version)
 
-        path = repository.get_typelib_path(self._namespace)
+        path = repository.get_typelib_path(self._pygi_namespace)
         return "<%s.%s %r from %r>" % (self.__class__.__module__,
                                       self.__class__.__name__,
-                                      self._namespace,
+                                      self._pygi_namespace,
                                       path)
diff --git a/gi/overrides/GIMarshallingTests.py b/gi/overrides/GIMarshallingTests.py
index 25a882f..a4ff593 100644
--- a/gi/overrides/GIMarshallingTests.py
+++ b/gi/overrides/GIMarshallingTests.py
@@ -21,7 +21,7 @@
 from ..overrides import override
 from ..importer import modules
 
-GIMarshallingTests = modules['GIMarshallingTests'].introspection_module
+GIMarshallingTests = modules['GIMarshallingTests']._pygi_introspection_module
 
 __all__ = []
 
diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py
index 78d8c35..9e4dc31 100644
--- a/gi/overrides/GLib.py
+++ b/gi/overrides/GLib.py
@@ -21,7 +21,7 @@
 from ..importer import modules
 from .._gi import variant_new_tuple, variant_type_from_string
 
-GLib = modules['GLib'].introspection_module
+GLib = modules['GLib']._pygi_introspection_module
 
 __all__ = []
 
diff --git a/gi/overrides/Gdk.py b/gi/overrides/Gdk.py
index 624ff49..4124914 100644
--- a/gi/overrides/Gdk.py
+++ b/gi/overrides/Gdk.py
@@ -22,7 +22,7 @@
 from ..overrides import override
 from ..importer import modules
 
-Gdk = modules['Gdk'].introspection_module
+Gdk = modules['Gdk']._pygi_introspection_module
 
 __all__ = []
 
@@ -43,7 +43,7 @@ class Color(Gdk.Color):
 Color = override(Color)
 __all__.append('Color')
 
-if Gdk.version == '2.0':
+if Gdk._pygi_version == '2.0':
     class Rectangle(Gdk.Rectangle):
 
         def __init__(self, x, y, width, height):
@@ -62,7 +62,7 @@ if Gdk.version == '2.0':
     Rectangle = override(Rectangle)
     __all__.append('Rectangle')
 
-if Gdk.version == '2.0':
+if Gdk._pygi_version == '2.0':
     class Drawable(Gdk.Drawable):
         def cairo_create(self):
             return Gdk.cairo_create(self)
@@ -111,7 +111,7 @@ class Event(Gdk.Event):
         Gdk.EventType.VISIBILITY_NOTIFY: 'visibility',
     }
 
-    if Gdk.version == '2.0':
+    if Gdk._pygi_version == '2.0':
         _UNION_MEMBERS[Gdk.EventType.NO_EXPOSE] = 'no_expose'
 
     def __new__(cls, *args, **kwargs):
@@ -129,7 +129,7 @@ __all__.append('Event')
 
 class DragContext(Gdk.DragContext):
     def finish(self, success, del_, time):
-        Gtk = modules['Gtk'].introspection_module
+        Gtk = modules['Gtk']._pygi_introspection_module
         Gtk.drag_finish(self, success, del_, time)
 
 DragContext = override(DragContext)
diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py
index 545e165..33d3de4 100644
--- a/gi/overrides/Gtk.py
+++ b/gi/overrides/Gtk.py
@@ -32,7 +32,7 @@ else:
     _basestring = basestring
     _callable = callable
 
-Gtk = modules['Gtk'].introspection_module
+Gtk = modules['Gtk']._pygi_introspection_module
 __all__ = []
 
 class Widget(Gtk.Widget):
diff --git a/gi/overrides/Pango.py b/gi/overrides/Pango.py
index 700d26d..41fe64a 100644
--- a/gi/overrides/Pango.py
+++ b/gi/overrides/Pango.py
@@ -21,7 +21,7 @@
 from ..overrides import override
 from ..importer import modules
 
-Pango = modules['Pango'].introspection_module
+Pango = modules['Pango']._pygi_introspection_module
 
 __all__ = []
 
diff --git a/gi/overrides/__init__.py b/gi/overrides/__init__.py
index c634a2f..b6fb50c 100644
--- a/gi/overrides/__init__.py
+++ b/gi/overrides/__init__.py
@@ -44,7 +44,7 @@ class overridefunc(object):
         if not hasattr(func, '__info__'):
             raise TypeError("func must be an gi function")
         from ..importer import modules
-        self.module = modules[func.__module__].introspection_module
+        self.module = modules[func.__module__]._pygi_introspection_module
 
     def __call__(self, func):
         def wrapper(*args, **kwargs):


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]