How can a module know the module that imported it?

Discussion in 'Python' started by kj, Nov 11, 2009.

  1. kj

    kj Guest

    The subject line says it all.

    Thanks!

    kynn
     
    kj, Nov 11, 2009
    #1
    1. Advertising

  2. kj

    Aahz Guest

    In article <hdf63i$cmp$>, kj <> wrote:
    >
    >The subject line says it all.


    You are probably trying to remove a screw with a hammer -- why don't you
    tell us what you really want to do and we'll come up with a Pythonic
    solution?
    --
    Aahz () <*> http://www.pythoncraft.com/

    [on old computer technologies and programmers] "Fancy tail fins on a
    brand new '59 Cadillac didn't mean throwing out a whole generation of
    mechanics who started with model As." --Andrew Dalke
     
    Aahz, Nov 11, 2009
    #2
    1. Advertising

  3. kj

    Ethan Furman Guest

    Aahz wrote:
    > In article <hdf63i$cmp$>, kj <> wrote:
    >
    >>The subject line says it all.

    >
    >
    > You are probably trying to remove a screw with a hammer -- why don't you
    > tell us what you really want to do and we'll come up with a Pythonic
    > solution?



    Well, I don't know what kj is trying to do, but my project is another
    (!) configuration program. (Don't worry, I won't release it... unless
    somebody is interested, of course !)

    So here's the idea so far:
    The configuration data is stored in a python module (call it
    settings.py). In order to be able to do things like add settings to it,
    save the file after changes are made, etc., settings.py will import the
    configuration module, called configure.py.

    A sample might look like this:

    <settings.py>
    import configure

    paths = configure.Item()
    paths.tables = 'c:\\app\\data'
    paths.temp = 'c:\\temp'
    </settings.py>

    And in the main program I would have:

    <some_app.py>
    import settings

    main_table = dbf.Table('%s\\main' % paths.tables)

    # user can modify path locations, and does, so update
    # we'll say it changes to \work\temp

    settings.paths.temp = user_setting()
    settings.configure.save()
    </some_app.py>

    And of course, at this point settings.py now looks like

    <settings.py>
    import configure

    paths = configure.Item()
    paths.tables = 'c:\\app\\data'
    paths.temp = 'c:\\work\\temp'
    </settings.py>

    Now, the tricky part is the line

    settings.configure.save()

    How will save know which module it's supposed to be re-writing? The
    solution that I have for now is

    def _get_module():
    "get the calling module -- should be the config'ed module"
    target = os.path.splitext(inspect.stack()[2][1])[0]
    target = __import__(target)
    return target

    If there's a better way, I'd love to know about it!

    Oh, and I'm using 2.5.4, but I suspect kj is using 2.6.

    ~Ethan~
     
    Ethan Furman, Nov 11, 2009
    #3
  4. kj

    kj Guest

    In <hdf9bq$59s$> (Aahz) writes:

    >In article <hdf63i$cmp$>, kj <> wrote:
    >>
    >>The subject line says it all.


    >You are probably trying to remove a screw with a hammer


    Worse: I'm trying to write Perl using Python!

    >-- why don't you
    >tell us what you really want to do and we'll come up with a Pythonic
    >solution?


    Because the problem that gave rise to this question is insignificant.
    I would want to know the answer in any case. *Can* it be done in
    Python at all?

    OK, if you must know:

    With Perl one can set a module-global variable before the module
    is loaded. This provides a very handy backdoor during testing.
    E.g.

    # in t/some_test.t script
    ....
    BEGIN { $My::Module::TESTING = 1; }
    use My::Module;
    ....

    and in My/Module.pm:

    package My::Module;
    our $TESTING ||= 0; # set to 0 unless already initialized to !0
    ....
    if ($TESTING) {
    # throw testing switches
    }


    This does not work in Python, because setting my.module.TESTING
    variable can happen only after my.module has been imported, but by
    this point, the module's top-level code has already been executed,
    so setting my.module.TESTING would have no effect. But one way to
    get a similar effect would be to have my.module set its TESTING
    (or whatever) variable equal to the value of this variable in the
    *importing* module.

    kynn
     
    kj, Nov 11, 2009
    #4
  5. > Because the problem that gave rise to this question is insignificant.
    > I would want to know the answer in any case. *Can* it be done in
    > Python at all?


    No.

    >
    > OK, if you must know:
    >
    > With Perl one can set a module-global variable before the module
    > is loaded. This provides a very handy backdoor during testing.
    > E.g.
    >
    > # in t/some_test.t script
    > ...
    > BEGIN { $My::Module::TESTING = 1; }
    > use My::Module;
    > ...
    >
    > and in My/Module.pm:
    >
    > package My::Module;
    > our $TESTING ||= 0; # set to 0 unless already initialized to !0
    > ...
    > if ($TESTING) {
    > # throw testing switches
    > }
    >
    >
    > This does not work in Python, because setting my.module.TESTING
    > variable can happen only after my.module has been imported, but by
    > this point, the module's top-level code has already been executed,
    > so setting my.module.TESTING would have no effect. But one way to
    > get a similar effect would be to have my.module set its TESTING
    > (or whatever) variable equal to the value of this variable in the
    > *importing* module.


    I don't understand enough (actually nothing) from perl, but I *am* a
    heavily test-driven developer in Python. And never felt that need.

    Sure, sometimes one wants to change behavior of a module under test,
    e.g. replacing something with a stub or some such.

    But where is the problem doing


    --- mytest.py ---

    import moduletobetested as m

    m.SOME_GLOBAL = "whatever"

    m.do_something()

    ---


    Diez
     
    Diez B. Roggisch, Nov 11, 2009
    #5
  6. On Wed, 11 Nov 2009 21:55:58 +0000, kj wrote:

    > With Perl one can set a module-global variable before the module is
    > loaded. This provides a very handy backdoor during testing. E.g.


    Any time somebody justifies a features as "a very handy backdoor", a
    billion voices cry out and then are suddenly silenced...

    [...]
    > This does not work in Python, because setting my.module.TESTING variable
    > can happen only after my.module has been imported, but by this point,
    > the module's top-level code has already been executed, so setting
    > my.module.TESTING would have no effect.


    (1) Then change your design so you're not relying on changing the
    behaviour of top-level code. Instead of:

    chant_incantation(arg) # effect module.magic, somehow...
    import module
    do_something_with( module.magic )

    (where magic is created by the top-level code)


    do this instead:

    import module
    do_something_with( module.make_magic(arg) )




    (2) If you still want a back-door, encapsulate it in another module:


    # Contents of module.py

    import _secret_backdoor
    if _secret_backdoor.manna > 17:
    magic = 42
    else:
    magic = 23


    Then in your calling code:

    # chant an incantation to change the behaviour of module
    import _secret_backdoor
    _secret_backdoor.manna = 1003

    import module
    do_something_with(module.magic)


    (3) Abuse __builtins__ by polluting it with your own junk:


    # Contents of module.py

    try:
    my_secret_name12761
    except NameError:
    magic = 23
    else:
    magic = 42



    Then in your calling code:

    # chant an incantation to change the behaviour of module
    import __builtins__
    __builtins__.my_secret_name12761 = None

    import module
    do_something_with(module.magic)


    But if you do this one, hundreds of Python developers carrying flaming
    torches and pitchforks will track you down and do terrible things to your
    corpse...


    *wink*




    --
    Steven
     
    Steven D'Aprano, Nov 11, 2009
    #6
  7. On Wed, 11 Nov 2009 13:44:06 -0800, Ethan Furman wrote:

    > Well, I don't know what kj is trying to do, but my project is another
    > (!) configuration program. (Don't worry, I won't release it... unless
    > somebody is interested, of course !)
    >
    > So here's the idea so far:
    > The configuration data is stored in a python module (call it
    > settings.py). In order to be able to do things like add settings to it,
    > save the file after changes are made, etc., settings.py will import the
    > configuration module, called configure.py.
    >
    > A sample might look like this:
    >
    > <settings.py>
    > import configure
    >
    > paths = configure.Item()
    > paths.tables = 'c:\\app\\data'
    > paths.temp = 'c:\\temp'
    > </settings.py>
    >
    > And in the main program I would have:
    >
    > <some_app.py>
    > import settings
    >
    > main_table = dbf.Table('%s\\main' % paths.tables)
    >
    > # user can modify path locations, and does, so update # we'll say it
    > changes to \work\temp
    >
    > settings.paths.temp = user_setting()
    > settings.configure.save()
    > </some_app.py>
    >
    > And of course, at this point settings.py now looks like
    >
    > <settings.py>
    > import configure
    >
    > paths = configure.Item()
    > paths.tables = 'c:\\app\\data'
    > paths.temp = 'c:\\work\\temp'
    > </settings.py>



    Self-modifying code?

    UNCLEAN!!! UNCLEAN!!!


    > Now, the tricky part is the line
    >
    > settings.configure.save()
    >
    > How will save know which module it's supposed to be re-writing?


    In my opinion, the cleanest way is to tell it which module to re-write.
    Or better still, tell it which *file* to write to:

    settings.configure.save(settings.__file__)

    which is still self-modifying, but at least it doesn't need magic to
    modify itself.




    --
    Steven
     
    Steven D'Aprano, Nov 12, 2009
    #7
  8. kj

    AK Eric Guest

    so:

    # moduleA.py
    import moduleB

    # moduleB.py
    import sys
    stuff = sys._getframe(1).f_locals
    print stuff

    Prints:

    {'__builtins__': <module '__builtin__' (built-in)>,
    '__file__': 'C:\\Documents and Settings\\<userName>\\My Documents\
    \python\\moduleA.py',
    '__name__': '__main__',
    '__doc__': None}

    Looks like you could query stuff['__file__'] to pull what you're
    after.
    ?
     
    AK Eric, Nov 12, 2009
    #8
  9. kj

    Ethan Furman Guest

    AK Eric wrote:
    > so:
    >
    > # moduleA.py
    > import moduleB
    >
    > # moduleB.py
    > import sys
    > stuff = sys._getframe(1).f_locals
    > print stuff
    >
    > Prints:
    >
    > {'__builtins__': <module '__builtin__' (built-in)>,
    > '__file__': 'C:\\Documents and Settings\\<userName>\\My Documents\
    > \python\\moduleA.py',
    > '__name__': '__main__',
    > '__doc__': None}
    >
    > Looks like you could query stuff['__file__'] to pull what you're
    > after.
    > ?


    The leading _ in _getframe indicates a private function to sys (aka
    implementation detail); in other words, something that could easily
    change between one Python version and the next.

    I'm using the inspect module (for the moment, at least), and my question
    boils down to: Will it work correctly on all versions of Python in the
    2.x range? 3.x range?

    ~Ethan~
     
    Ethan Furman, Nov 12, 2009
    #9
  10. kj

    AK Eric Guest

    On Nov 12, 10:10 am, Ethan Furman <> wrote:
    > AK Eric wrote:
    > > so:

    >
    > > # moduleA.py
    > > import moduleB

    >
    > > # moduleB.py
    > > import sys
    > > stuff = sys._getframe(1).f_locals
    > > print stuff

    >
    > > Prints:

    >
    > > {'__builtins__': <module '__builtin__' (built-in)>,
    > > '__file__': 'C:\\Documents and Settings\\<userName>\\My Documents\
    > > \python\\moduleA.py',
    > > '__name__': '__main__',
    > > '__doc__': None}

    >
    > > Looks like you could query stuff['__file__'] to pull what you're
    > > after.
    > > ?

    >
    > The leading _ in _getframe indicates a private function to sys (aka
    > implementation detail); in other words, something that could easily
    > change between one Python version and the next.
    >
    > I'm using the inspect module (for the moment, at least), and my question
    > boils down to:  Will it work correctly on all versions of Python in the
    > 2.x range?  3.x range?
    >


    Good point, I totally missed that. Someone had passed that solution
    to me at one point and I was so excited I kind of looked that over :p
     
    AK Eric, Nov 12, 2009
    #10
    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. Martin M.
    Replies:
    4
    Views:
    367
    Simon Brunning
    Dec 15, 2005
  2. Visco Shaun
    Replies:
    2
    Views:
    236
    John Machin
    Apr 17, 2009
  3. Dun Peal
    Replies:
    10
    Views:
    472
    Chris Rebert
    May 3, 2011
  4. Andries

    I know, I know, I don't know

    Andries, Apr 23, 2004, in forum: Perl Misc
    Replies:
    3
    Views:
    276
    Gregory Toomey
    Apr 23, 2004
  5. Volker Nicolai
    Replies:
    9
    Views:
    1,122
    Fabian Pilkowski
    Jul 4, 2005
Loading...

Share This Page