CONSTRUCT - Python's way of Ruby's "alias_method"

Discussion in 'Python' started by Ilias Lazaridis, Jun 8, 2006.

  1. I have a few small questions subjecting python functionality, most
    importantly the "alias_method".

    -

    *IMPORT*

    I would like to know, if this construct is valid, or if it can result in
    problems (that I do not see as a newcomer):

    1082 try:
    1083 from django.rework.evolve import evolvedb
    1084 except ImportError:
    1085 def evolvedb():
    1086 "Evolve Command Dummy"
    1087 print 'Command evolvedb not imported'
    1088 evolvedb.args =''

    -

    *PATCHING*

    A second problem is, how to make the setup for users (testers) more
    convenient. Is there e.g. any mechanism to apply a patch in an automated
    manner (e.g. using a python library)?

    -

    *ALIAS_METHOD*

    The django commands are hard-coded:

    http://code.djangoproject.com/browser/django/trunk/django/core/management.py#L1180

    thus elegant/dynamic additions of commands seem not possible.

    Another possibility is to enlink (hook?) the functionality into an
    existent function

    Is there any way (beside a patch) to alter the behaviour to an existing
    function. Is ther a python construct similar to the "alias_method" of Ruby:

    (example from an simple evolution support for a ruby orm)

    #------------------------------------------------------------------------------
    # use "alias_method" to enlink the code
    #------------------------------------------------------------------------------

    class SqliteAdapter
    alias_method :eek:ld_create_table, :create_table
    def create_table(*args)
    table_evolve(*args)
    result = old_create_table(*args)
    return result
    end
    end

    http://lazaridis.com/case/persist/og-evolve.rb

    -
    -
    -

    If anyone is interested to verify the results in order to stabelize the
    simple schema evolution support for django, please review the results
    here:

    http://case.lazaridis.com/wiki/DjangoProductEvaluation
    http://case.lazaridis.com/wiki/DjangoSchemaEvolution
    http://case.lazaridis.com/browser/django/rework/evolve.py
    http://case.lazaridis.com/browser/django/rework/add_evolvedb_command.diff

    ..

    --
    http://lazaridis.com
    Ilias Lazaridis, Jun 8, 2006
    #1
    1. Advertising

  2. Le Jeudi 08 Juin 2006 14:28, Ilias Lazaridis a écrit :
    > Another possibility is to enlink (hook?) the functionality into an
    > existent function
    >
    > Is there any way (beside a patch) to alter the behaviour to an existing
    > function. Is ther a python construct similar to the "alias_method" of Ruby:
    >

    No, there is no special construct to do this, but we do things very similar
    every day in Zope, it's called "monkey patch" :

    #patch_service.py
    from toto import service

    def my_impl(self, *args) :
    old_result = self._old_method(*args)
    # ...
    return new_result

    if not hasattr(service, '_old_method') :
    service._old_method = service.method
    service.method = my_impl

    once this file is imported, all future calls to "method" of service instances
    will use my_impl.

    --
    _____________

    Maric Michaud
    _____________

    Aristote - www.aristote.info
    3 place des tapis
    69004 Lyon
    Tel: +33 426 880 097
    Maric Michaud, Jun 8, 2006
    #2
    1. Advertising

  3. Since your question is so much about Django, you might want to ask on
    Django groups.

    Oops, you're not welcome there anymore, almost forgot.

    But if merely reading the subject of a posting I already know who's the
    poster, it's perhaps a bad sign.

    Further readers of this thread might be interested in the discussions
    linked from these pages:

    http://en.wikipedia.org/wiki/Ilias_Lazaridis
    http://www.encyclopediadramatica.com/index.php/Ilias
    http://groups.google.com/group/comp...vc=1&q=ilias EVALUATION&#doc_ae6315fda51d50a1

    EOT?
    Tim N. van der Leeuw, Jun 8, 2006
    #3
  4. Ilias Lazaridis

    Duncan Booth Guest

    Ilias Lazaridis wrote:

    > Is there any way (beside a patch) to alter the behaviour to an
    > existing function. Is ther a python construct similar to the
    > "alias_method" of Ruby:


    This is a Python list. Would you care to explain what alias_method does?

    >
    > (example from an simple evolution support for a ruby orm)
    >
    > #----------------------------------------------------------------------
    > -------- # use "alias_method" to enlink the code
    > #----------------------------------------------------------------------
    > --------
    >
    > class SqliteAdapter
    > alias_method :eek:ld_create_table, :create_table
    > def create_table(*args)
    > table_evolve(*args)
    > result = old_create_table(*args)
    > return result
    > end
    > end
    >


    This looks like alias_method does nothing much more than an assignment. If
    you want to override a method in a base class then you can do it something
    like:

    class SqliteAdapter(BaseClass):
    old_create_table = BaseClass.create_table
    def create_table(self, *args)
    self.table_evolve(*args)
    result = self.old_create_table(*args)
    return result

    but the more usual way is just to call the original method directly in the
    base class.

    class SqliteAdapter(BaseClass):
    def create_table(self, *args)
    self.table_evolve(*args)
    result = BaseClass.create_table(self, *args)
    return result

    If that isn't what you are trying to achieve you'll have to explain more.
    Duncan Booth, Jun 8, 2006
    #4
  5. Ilias Lazaridis

    Duncan Booth Guest

    Ilias Lazaridis wrote:

    > I would like to know, if this construct is valid, or if it can result in
    > problems (that I do not see as a newcomer):
    >
    > 1082 try:
    > 1083 from django.rework.evolve import evolvedb
    > 1084 except ImportError:
    > 1085 def evolvedb():
    > 1086 "Evolve Command Dummy"
    > 1087 print 'Command evolvedb not imported'
    > 1088 evolvedb.args =''


    The only real problem here is that if django.rework.evolve imports
    something else which doesn't exist you get your fallback code instead of
    reporting the error. In other words there is a chance that you could mask a
    deeper problem.

    If this worries you then you could do:

    try:
    from django.rework.evolve import evolvedb
    except ImportError, e:
    if str(e).rsplit(' ')[-1] != 'django.rework.evolve':
    raise
    ... rest of code here ...
    Duncan Booth, Jun 8, 2006
    #5
  6. Le Jeudi 08 Juin 2006 15:15, Duncan Booth a écrit :
    > but the more usual way is just to call the original method directly in the
    > base class.
    >
    > class SqliteAdapter(BaseClass):
    > def create_table(self, *args)
    > self.table_evolve(*args)
    > result = BaseClass.create_table(self, *args)
    > return result
    >


    Yeah, this the right way to reuse ancestor's implementation of a method.

    > If that isn't what you are trying to achieve you'll have to explain more.

    I'm not a ruby programmer, but I understood it like this : the prupose is to
    modify the behavior of an existing third-party class, in all application
    (even in existing third party modules), without any code modifications
    (traditional patch) in those modules.

    Your proposal is not as good here, assuming BaseClass is defined in module
    toto, you can still do toto.BaseClass = SqliteAdapter, but you must ensure
    that this code is imported before any other where classes inherit from
    BaseClass. The one I porpose in my other post is robust, several packages can
    even patch the same method with no side effects.


    --
    _____________

    Maric Michaud
    _____________

    Aristote - www.aristote.info
    3 place des tapis
    69004 Lyon
    Tel: +33 426 880 097
    Maric Michaud, Jun 8, 2006
    #6
  7. Ilias Lazaridis, Jun 8, 2006
    #7
  8. Maric Michaud wrote:
    > Le Jeudi 08 Juin 2006 15:15, Duncan Booth a écrit :
    >> but the more usual way is just to call the original method directly in the
    >> base class.
    >>
    >> class SqliteAdapter(BaseClass):
    >> def create_table(self, *args)
    >> self.table_evolve(*args)
    >> result = BaseClass.create_table(self, *args)
    >> return result
    >>

    >
    > Yeah, this the right way to reuse ancestor's implementation of a method.
    >
    >> If that isn't what you are trying to achieve you'll have to explain more.

    > I'm not a ruby programmer, but I understood it like this : the prupose is to
    > modify the behavior of an existing third-party class, in all application
    > (even in existing third party modules), without any code modifications
    > (traditional patch) in those modules.


    yes, you've understood right.

    > Your proposal is not as good here, assuming BaseClass is defined in module
    > toto, you can still do toto.BaseClass = SqliteAdapter, but you must ensure
    > that this code is imported before any other where classes inherit from
    > BaseClass. The one I porpose in my other post is robust, several packages can
    > even patch the same method with no side effects.


    Your suggestion is most possibly the relevant construct.

    I'll post a note after changing the implementation.

    Thank's a lot!

    ..

    --
    http://lazaridis.com
    Ilias Lazaridis, Jun 8, 2006
    #8
  9. Duncan Booth wrote:
    > Ilias Lazaridis wrote:
    >
    >> I would like to know, if this construct is valid, or if it can result in
    >> problems (that I do not see as a newcomer):
    >>
    >> 1082 try:
    >> 1083 from django.rework.evolve import evolvedb
    >> 1084 except ImportError:
    >> 1085 def evolvedb():
    >> 1086 "Evolve Command Dummy"
    >> 1087 print 'Command evolvedb not imported'
    >> 1088 evolvedb.args =''

    >
    > The only real problem here is that if django.rework.evolve imports
    > something else which doesn't exist you get your fallback code instead of
    > reporting the error. In other words there is a chance that you could mask a
    > deeper problem.
    >
    > If this worries you then you could do:
    >
    > try:
    > from django.rework.evolve import evolvedb
    > except ImportError, e:
    > if str(e).rsplit(' ')[-1] != 'django.rework.evolve':
    > raise
    > ... rest of code here ...


    I thin I understand.

    If the exception is _not_ caused by 'django.rework.evolve', then it will
    be raised, thus I can see what caused it.

    Very nice!

    ..

    --
    http://lazaridis.com
    Ilias Lazaridis, Jun 8, 2006
    #9
  10. On Thu, 08 Jun 2006 06:05:35 -0700
    "Tim N. van der Leeuw" <> wrote:

    #> Since your question is so much about Django, you might want to ask
    #> on Django groups.

    Now *that*, and the rest of your post, was uncalled for.

    This thread is perfectly on topic for this list and the question asked
    concerns *Python*, with django being only a use case.

    --
    Best wishes,
    Slawomir Nowaczyk
    ( )

    Some drink at the fountain of knowledge. Others just gargle.
    Slawomir Nowaczyk, Jun 8, 2006
    #10
  11. On Thu, 08 Jun 2006 15:28:39 +0300
    Ilias Lazaridis <> wrote:

    #> *IMPORT*
    #>
    #> I would like to know, if this construct is valid, or if it can
    #> result in problems (that I do not see as a newcomer):

    The intricacies of import are far beyond me, but FWIW I do not see
    anything wrong in this code.

    #> Is there any way (beside a patch) to alter the behaviour to an
    #> existing function.

    You can just assign new function to the old name. There are a few
    loops to hop through if you want to alter the behaviour of an existing
    *method*... There is an example -- I do not know if a good one -- here:
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52192

    --
    Best wishes,
    Slawomir Nowaczyk
    ( )

    Strange how people who don't even know their neighbors
    are extremely curious to know if there's extra-terrestrial life.
    Slawomir Nowaczyk, Jun 8, 2006
    #11
  12. Maric Michaud wrote:
    > Le Jeudi 08 Juin 2006 14:28, Ilias Lazaridis a écrit :
    >> Another possibility is to enlink (hook?) the functionality into an
    >> existent function
    >>
    >> Is there any way (beside a patch) to alter the behaviour to an existing
    >> function. Is ther a python construct similar to the "alias_method" of Ruby:
    >>

    > No, there is no special construct to do this, but we do things very similar
    > every day in Zope, it's called "monkey patch" :
    >
    > #patch_service.py
    > from toto import service
    >
    > def my_impl(self, *args) :
    > old_result = self._old_method(*args)
    > # ...
    > return new_result
    >
    > if not hasattr(service, '_old_method') :
    > service._old_method = service.method
    > service.method = my_impl
    >
    > once this file is imported, all future calls to "method" of service instances
    > will use my_impl.


    Ok, just a small problem when a _function_ is to be hooked.

    Looking a the code in the debugger shows that the function "syncdb" is
    correctly overridden. But when the code returns, "syncdb" has again it's
    original value.

    Can I import "syncdb" by reference instead by value, thus the change
    'survives'?

    #------------------------------------------------------------------------------

    #syncdb_hook.py

    from django.rework.evolve import evolvedb
    from django.core.management import syncdb

    def syncdb_new(*args) :
    evolvedb()
    syncdb_result = syncdb_old(*args)
    return syncdb_result

    if syncdb != syncdb_new:
    syncdb_old = syncdb
    syncdb = syncdb_new

    ..

    --
    http://lazaridis.com
    Ilias Lazaridis, Jun 9, 2006
    #12
  13. Ilias Lazaridis

    Duncan Booth Guest

    Ilias Lazaridis wrote:

    >> #patch_service.py
    >> from toto import service
    >>
    >> def my_impl(self, *args) :
    >> old_result = self._old_method(*args)
    >> # ...
    >> return new_result
    >>
    >> if not hasattr(service, '_old_method') :
    >> service._old_method = service.method
    >> service.method = my_impl
    >>
    >> once this file is imported, all future calls to "method" of service
    >> instances will use my_impl.

    >
    > Ok, just a small problem when a _function_ is to be hooked.
    >
    > Looking a the code in the debugger shows that the function "syncdb" is
    > correctly overridden. But when the code returns, "syncdb" has again
    > it's original value.
    >
    > Can I import "syncdb" by reference instead by value, thus the change
    > 'survives'?


    The difference is that Maric imported the module. To make the change affect
    the original model you have to access the function as an attribute of its
    module, not by importing the function from the module.
    >
    > #----------------------------------------------------------------------
    > --------
    >
    > #syncdb_hook.py
    >
    > from django.rework.evolve import evolvedb
    > from django.core.management import syncdb

    from django.core import management

    >
    > def syncdb_new(*args) :
    > evolvedb()
    > syncdb_result = syncdb_old(*args)
    > return syncdb_result
    >
    > if syncdb != syncdb_new:
    > syncdb_old = syncdb
    > syncdb = syncdb_new
    >

    if management.syncdb != syncdb_new:
    syncdb_old = management.syncdb
    management.syncdb = syncdb_new
    Duncan Booth, Jun 9, 2006
    #13
  14. Duncan Booth wrote:
    > Ilias Lazaridis wrote:
    >
    >>> #patch_service.py
    >>> from toto import service
    >>>
    >>> def my_impl(self, *args) :
    >>> old_result = self._old_method(*args)
    >>> # ...
    >>> return new_result
    >>>
    >>> if not hasattr(service, '_old_method') :
    >>> service._old_method = service.method
    >>> service.method = my_impl
    >>>
    >>> once this file is imported, all future calls to "method" of service
    >>> instances will use my_impl.

    >> Ok, just a small problem when a _function_ is to be hooked.
    >>
    >> Looking a the code in the debugger shows that the function "syncdb" is
    >> correctly overridden. But when the code returns, "syncdb" has again
    >> it's original value.
    >>
    >> Can I import "syncdb" by reference instead by value, thus the change
    >> 'survives'?

    >
    > The difference is that Maric imported the module. To make the change affect
    > the original model you have to access the function as an attribute of its
    > module, not by importing the function from the module.


    ok, I understand.

    the code below works, but has the limitation that I cannot import the
    syncdb_hook within "django.core.management".

    There is no way to import/get "syncdb" but mutable?

    >> #syncdb_hook.py
    >>
    >> from django.rework.evolve import evolvedb
    >> from django.core.management import syncdb

    > from django.core import management
    >
    >> def syncdb_new(*args) :
    >> evolvedb()
    >> syncdb_result = syncdb_old(*args)
    >> return syncdb_result
    >>
    >> if syncdb != syncdb_new:
    >> syncdb_old = syncdb
    >> syncdb = syncdb_new
    >>

    > if management.syncdb != syncdb_new:
    > syncdb_old = management.syncdb
    > management.syncdb = syncdb_new


    works fine.

    ..

    --
    http://lazaridis.com
    Ilias Lazaridis, Jun 9, 2006
    #14
  15. Slawomir Nowaczyk wrote:
    > On Thu, 08 Jun 2006 15:28:39 +0300
    > Ilias Lazaridis <> wrote:
    >
    > #> *IMPORT*
    > #>
    > #> I would like to know, if this construct is valid, or if it can
    > #> result in problems (that I do not see as a newcomer):
    >
    > The intricacies of import are far beyond me, but FWIW I do not see
    > anything wrong in this code.
    >
    > #> Is there any way (beside a patch) to alter the behaviour to an
    > #> existing function.
    >
    > You can just assign new function to the old name. There are a few
    > loops to hop through if you want to alter the behaviour of an existing
    > *method*... There is an example -- I do not know if a good one -- here:
    > http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52192


    This looks intresting, especially the comment, although I do not
    understand what is meant by "creates a cycle in the object".

    "
    There is a Better Way to Add Methods To Classes, Moshe Zadka, 2001/03/15
    This method creates a cycle in the object for no reason at all. The
    following function will add any function to an instance in a cycle free way:

    def add_method(self, method, name=None):
    if name is None: name = method.func_name
    class new(self.__class__): pass
    setattr(new, name, method)
    self.__class__ = new

    Use as follows:

    def pretty_str(self): pass

    add_method(C(), pretty_str, '__str__')
    "

    --
    http://lazaridis.com
    Ilias Lazaridis, Jun 9, 2006
    #15
  16. Le Vendredi 09 Juin 2006 20:06, Ilias Lazaridis a écrit :
    > the code below works, but has the limitation that I cannot import the
    > syncdb_hook within "django.core.management".


    In [4]: from b import CONS

    In [5]: import b

    In [6]: b.CONS = 3

    In [7]: CONS
    Out[7]: 5

    In [8]: from b import CONS

    In [9]: CONS
    Out[9]: 3

    So, if you change one module name, a function or a class or a constant, you
    must do it before it is imported, or you must reload modules using it. But
    either are not always possible, and the later is not what you want to achieve
    here as it will re-execute all initialisation code in those modules.

    But think of that, a function is hopefully an object in python, hmmm :

    In [1]: from temp import func

    In [2]: func(5)
    Out[2]: 5

    In [3]: def g(s) : return s*2
    ...:

    In [4]: func.func_code = g.func_code

    In [5]: func(5)
    Out[5]: 10

    hey, that should work !

    --
    _____________

    Maric Michaud
    _____________

    Aristote - www.aristote.info
    3 place des tapis
    69004 Lyon
    Tel: +33 426 880 097
    Maric Michaud, Jun 10, 2006
    #16
  17. Maric Michaud wrote:
    > Le Vendredi 09 Juin 2006 20:06, Ilias Lazaridis a écrit :
    >> the code below works, but has the limitation that I cannot import the
    >> syncdb_hook within "django.core.management".

    >
    > In [4]: from b import CONS
    >
    > In [5]: import b
    >
    > In [6]: b.CONS = 3
    >
    > In [7]: CONS
    > Out[7]: 5
    >
    > In [8]: from b import CONS
    >
    > In [9]: CONS
    > Out[9]: 3
    >
    > So, if you change one module name, a function or a class or a constant, you
    > must do it before it is imported, or you must reload modules using it. But
    > either are not always possible, and the later is not what you want to achieve
    > here as it will re-execute all initialisation code in those modules.
    >
    > But think of that, a function is hopefully an object in python, hmmm :
    >
    > In [1]: from temp import func
    >
    > In [2]: func(5)
    > Out[2]: 5
    >
    > In [3]: def g(s) : return s*2
    > ...:
    >
    > In [4]: func.func_code = g.func_code
    >
    > In [5]: func(5)
    > Out[5]: 10
    >
    > hey, that should work !


    Great Construct! Much flexibility!

    I'll try the implementation tomorrow.

    -

    The actual Versions of the hooks can be found here:

    http://case.lazaridis.com/browser/django/rework/syncdb_hook.py?rev=7
    http://case.lazaridis.com/browser/django/rework/startproject_hook.py?rev=13

    This construct has helped to simplify nearly all simplification goals:

    http://case.lazaridis.com/wiki/DjangoSchemaEvolution

    ..

    --
    http://lazaridis.com
    Ilias Lazaridis, Jun 10, 2006
    #17
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Mukesh
    Replies:
    4
    Views:
    618
    Paul N
    Mar 26, 2010
  2. Angel Martin

    alias or alias_method problem

    Angel Martin, Apr 29, 2004, in forum: Ruby
    Replies:
    6
    Views:
    123
    Kristof Bastiaensen
    Apr 29, 2004
  3. Kelly Felkins
    Replies:
    1
    Views:
    117
    Robert Klemme
    Aug 9, 2005
  4. Trans
    Replies:
    0
    Views:
    86
    Trans
    Nov 23, 2005
  5. Ara.T.Howard

    alias_method 'stickiness'

    Ara.T.Howard, Mar 13, 2006, in forum: Ruby
    Replies:
    1
    Views:
    90
Loading...

Share This Page