2

For previous versions of the question see the revision history - managed to come up with a minimal executable example that reproduces the problem

# module test.py
import shlex
from test2 import Parser

class Test(object):
    sync_client = None

    def __init__(self):
        self.__class__.sync_client = 1
        parser = Parser(description='desc') # was before the assignment - egal
        while True:
            cmd = shlex.split(raw_input('> ').strip())
            parser.parse_args(cmd)

    @classmethod
    def newRequestClient(cls):
        print cls.sync_client # None

if __name__ == "__main__":
    Test()

# module test2.py
import argparse

class Share(object):
    class _ShareAction(argparse.Action):
        def __call__(self, parser, namespace, values, option_string=None):
            from test import Test
            print Test.sync_client # None
            Test.newRequestClient()

    def __call__(self, subparsers):
        parser_a = subparsers.add_parser('name')
        parser_a.add_argument(dest='dest', help='help2',
                              action=Share._ShareAction)

class Parser(argparse.ArgumentParser):
    def __init__(self, description, add_h=True):
        super(Parser, self).__init__(description=description, add_help=add_h)
        subparsers = self.add_subparsers(title='Commands')
        subparsers._parser_class = argparse.ArgumentParser
        Share()(subparsers)

Run test.py and type name 123 in the prompt to see the Nones printed. Well maybe it is something obvious - it's been hours :)

EDIT: For the reproducer posted here this:

if __name__ == "__main__":
    from test import Test
    Test()

works. Adding from sync import Sync (and variations) in my main did not help however. I finally "solved" it by:

class Share(Command):
    class _ShareAction(argparse.Action):
        def __call__(self, parser, namespace, values, option_string=None):
            sync = sys.modules['__main__']
            print sync.Sync.sync_client # <SyncClient(SyncClient, started 6084)>
            sync.Sync.newRequestClient(host=namespace.host, repo=values)

But I do not yet fully understand why from sync import Sync did not work.

FINALLY: thanks to a comment by @MartijnPieters:

# module watcher.sync

if __name__ == "__main__":
    from watcher.sync import Sync
    Sync().main()

This looks ugly though so feel free to comment (or even add an answer) on how I could avoid it.

Community
  • 1
  • 1
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
  • 1
    Your new example involves a lot of other complications. How are you using those classes? – BrenBarn Sep 21 '14 at 21:09
  • I don't see an instance of `Sync` ever being created in your new code. – Lukas Graf Sep 21 '14 at 21:14
  • @BrenBarn: will edit with more details but what especially you want to know ? – Mr_and_Mrs_D Sep 21 '14 at 21:19
  • 2
    the **minimal executable** example that **reproduces** your problem – joaquin Sep 21 '14 at 21:21
  • @joaquin: Working on this - it is not trivial though – Mr_and_Mrs_D Sep 21 '14 at 21:22
  • @Mr_and_Mrs_D: You just showed class definitions. You didn't show any code that actually does anything with those classes. – BrenBarn Sep 21 '14 at 21:23
  • @BrenBarn: the work is done in `while True: cmd = shlex.split(raw_input('> ').strip()) parser.parse(cmd)` - command line app - accepts commands and does things (via the actions specified for the commands). Commands are called but for some reason `print Sync.sync_client # None`. Of course I did try many variations (@staticmethod among others) – Mr_and_Mrs_D Sep 21 '14 at 21:28
  • @Mr_and_Mrs_D as I said above, you never create an instance of `Sync` in your code (or you omitted that part of the code) - therefore `Sync.__init__()` is never called. – Lukas Graf Sep 21 '14 at 21:58
  • @LukasGraf: I thought is was clear that the `while True` loop in Sync.init is the app - will edit – Mr_and_Mrs_D Sep 21 '14 at 22:03

1 Answers1

3

You misspelled __init__:

def __int__(self):

You are missing an i and the method is simply not called. Correct the error and your test works.

Demo:

>>> class Test(object):
...     class_attr = None
...     def __init__(self):  # note the spelling
...         self.__class__.class_attr = 1
...     @staticmethod
...     def static_meth():
...         print Test.class_attr
...     @classmethod
...     def class_meth(cls):
...         print cls.class_attr
... 
>>> t = Test()
>>> Test.class_attr
1
>>> Test.static_meth()
1
>>> Test.class_meth()
1
>>> t.class_meth()
1
>>> t.static_meth()
1

In you updated code you have two issues:

First you create an instance of Parser before the class attribute has been set. The self.__class__.sync_client = 1 line simply hasn't executed yet when Parser.__init__ is being called.

You then confuse the main script and the test module. Python imports the main script as __main__, not test. If you move the Test class out to a separate module or use from __main__ import Test your code will work.

See Importing modules: __main__ vs import as module

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Well this is true - in my real class hadn't mispelled `__init__` though - let me see a minute – Mr_and_Mrs_D Sep 21 '14 at 20:38
  • 1
    @Mr_and_Mrs_D: you misspelled *something*; see my demo (from a 2.7.8 session). – Martijn Pieters Sep 21 '14 at 20:38
  • I edited the question with my original code - will keep looking but I hope you see something I don't :) – Mr_and_Mrs_D Sep 21 '14 at 21:09
  • Well - may be an import issue ? I do `from watcher.sync import Sync` and this is run before `Sync.__init__()` runs - at that time the debugger shows me a Sync class that has `sync_client == None` - could it be this ? – Mr_and_Mrs_D Sep 21 '14 at 21:41
  • @Mr_and_Mrs_D sounds like you are perhaps importing a *different* module / class from what you *think* you are importing. Use the `pdb` module, step through carefully, triple check the `__module__` attributes, etc. – Martijn Pieters Sep 21 '14 at 22:21
  • Well I am proud to have posted a reproducing example (and no it was the Sync class - I saw it in Pycharm debugger with my own eyes, None and all - the attribute was there but unset) – Mr_and_Mrs_D Sep 21 '14 at 22:28
  • Does not help moving it below (had tried in my original code but did not move it to the reproducer - oops) – Mr_and_Mrs_D Sep 21 '14 at 22:30
  • @Mr_and_Mrs_D found your issue. Sorry for the brevity; this all from a smart phone. – Martijn Pieters Sep 21 '14 at 22:37
  • Wait I'll digest this +1 :D - new to python and all that - you can imagine my confusion – Mr_and_Mrs_D Sep 21 '14 at 22:39
  • See my edits: the reproducer only needed `from test import Test` - but my class needed the hack I posted - do not fully understand why. – Mr_and_Mrs_D Sep 21 '14 at 23:48
  • @Mr_and_Mrs_D: are you using a script *inside a package* there? `from watcher.sync import Sync` suggests so. You now have three elements: `__main__`, `sync` and `watcher.sync`. Both `watcher` and `sync` are top-level packages there. You should not be running a script from inside a package, certainly not and expect it to still be in the package namespace. – Martijn Pieters Sep 22 '14 at 06:55
  • Oh thanks again :) Yep that was it ! Could you point me to some docs detailing good practices with scripts ? From my few experience with python I thought that a python app is deep down a `if __name__ == "__main__": SomeClass().main()` - but apparently it is not so. Thanks again for saving my sanity ! – Mr_and_Mrs_D Sep 22 '14 at 10:41
  • @Mr_and_Mrs_D: yes, testing for `__main__` is good practice for scripts; but putting scripts into your packages doesn't work as you expect it to, especially when you then start mixing up relative and absolute imports. – Martijn Pieters Sep 22 '14 at 10:47
  • @Mr_and_Mrs_D: not sure about a docs pointer; I've already pointed you to answers of mine that explain how things work underneath, which I personally find much more helpful to then derive best practices from. – Martijn Pieters Sep 22 '14 at 10:48
  • Yes thanks - I did read and it was really helpful (I took the `sys.modules['__main__']` hack from the comments there !). It is the scripts part that now escapes me - as in, is `if __name__ == "__main__"` frowned upon (I mean as an app design) ? – Mr_and_Mrs_D Sep 22 '14 at 10:53
  • 1
    @Mr_and_Mrs_D: no, that is not frowned upon. What won't work is putting the script file inside a package, and then expect to be part of the package namespace. – Martijn Pieters Sep 22 '14 at 10:59