Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Global variable is undefined at the module level

(There are many similar and more generic questions, been trying the solutions from them after reading through them, can't get them working so asking here as a more situation-specific version of what I'm seeing)

I think I am really miss-understanding how Python does OOP due to my more C#/C++ background. So here's what I'm trying to do right this moment.

I'm starting with two modules to set up the rest of my project, partially as a sanity-check and proof-of-concept. One module logs things to a file as I go while also storing data from multiple modules (to eventually package them all and dump them on request) Doing all this in PyCharm and mentioning the error warnings it suggests by the way, and using Python 2.7

Module 1:
  src\helpers\logHelpers.py
    class LogHelpers:
      class log:
         def classEnter():
           #doing stuff
         def __init__(self):
           self.myLog = LogHelpers.log()  #forgot to mention this was here initially
      [..] various logging functions and variables to summarize what's happening
    __builtin__.mylogger = LogHelpers

Module 2:
  src\ULTs\myULTs.py
    mylogger.myLog.classEnter()

(both the modules and the root src\ have an empty init.py file in them)

So according to the totally awesome response here ( Python - Visibility of global variables in imported modules ) at this stage this should be working, but 'mylogger' becomes an 'unresolved reference'

So that was one approach. I also tried the more straight forward global one ( Python: How to make a cross-module variable? )

Module 1:
  src\helpers\logHelpers.py
    class LogHelpers:
      class log:
         def classEnter(self):
           #doing stuff
         def __init__(self):
           self.myLog = LogHelpers.log()  #forgot to mention this was here initially
      [..] various logging functions and variables to summarize what's happening
    mylogger = LogHelpers

  __init__.py
     __all__ = ['LogHelpers', hexlogger]
    from .logHelpers import * 

Module 2:
  src\ULTs\myULTs.py
    from helpers import mylogger
    mylogger.myLog.classEnter()

This version gets a "parameter 'self' unfilled" error on the classEnter, which various reports seem to indicate means that mylogger is un-initialized (misleading error code but that's what it seems to mean)

And then I tried this..

Module 1:
  src\helpers\logHelpers.py
    class LogHelpers:
      class log:
         def classEnter(self):
           #doing stuff
         def __init__(self):
           self.myLog = LogHelpers.log()  #forgot to mention this was here initially
      [..] various logging functions and variables to summarize what's happening
    __mylogger = LogHelpers

  __init__.py
     __all__ = ['LogHelpers', hexlogger]
    from .logHelpers import * 

Module 2:
  src\ULTs\myULTs.py
    from helpers import mylogger

    def someFunction(self):
      global mylogger
      mylogger.myLog.classEnter()

And this version gets the 'Global variable is undefined at the module level' error when I hover of global mylogger.

Then there is the idea of each other module tracking its own instance of a class apparently, if I end up having to I can go with that method and coordinate them.. but that's kind of a hack considering what I'm trying to do.

That's kind of where I'm at, that's the gist of what I'm trying to do... I'm reading through as many similar questions as I can but all of them seem to come back to these kinda of solutions (which don't seem to be working) or saying 'don't do that' (which is generally good advice but I'm not really grocking the preferred Pythony way of keeping multiple ongoing non-static classes organized for a large project - other than shoving them all in one directory)

Thoughts? (How badly am I mangling Python here?)

[EDIT] Based on feedback tried a mini version that eliminated the inner classes completely: Ok, so did a local mini-class based on what you said:

class testClass:
    def __init__(self):
        self.testVar = 2
    def incrementVar(self):
        self.testVar += 1
myClass = testClass()

Set it up via init.py

__all__ = [myClass]
from .logHelpers import myClass

Went to other module and

from helpers import myClass
class Test_LogHelpers(unittest.TestCase):
    def test_mini(self):
        myClass.incrementVar()

Ran it directly instead of looking at PyCharm, no Global anything.. NameError: name 'myClass is not defined

So still at square one :( (and still need to store state)

[EDIT] Adding Traceback:

Traceback (most recent call last):
  File "C:\Program Files (x86)\JetBrains\PyCharm Community Edition 3.4.1\helpers\pycharm\utrunner.py", line 124, in <module>    module = loadSource(a[0])
  File "C:\Program Files (x86)\JetBrains\PyCharm Community Edition 3.4.1\helpers\pycharm\utrunner.py", line 40, in loadSource    module = imp.load_source(moduleName, fileName)
  File "C:\[...mylocation...]\py\src\ULTs\LogHelpers_ULT.py", line 3, in <module>    from helpers import myClass
  File "C:\[...mylocation...]\py\src\helpers\__init__.py", line 7, in <module>
    __all__ = [myClass]
NameError: name 'myClass' is not defined

============================================================================

kk, I got it working with the miniclass. I don't know why the other approach / approaches was not working, but this seemed to fix things. (Resources: http://docs.python-guide.org/en/latest/writing/structure/ , http://mikegrouchy.com/blog/2012/05/be-pythonic-__init__py.html )

**logHelpers.py**
[... some static logging functionality ...]

class testClass:
    def __init__(self):
        self.testVar = 2

    def incrementVar(self, source):
        self.testVar += 1
        mylogger.myLog.info(source + " called, new val: " + str(self.testVar))
myClass = testClass()

**test_LogHelpers_ULT.py**
import unittest

from helpers.logHelpers import myClass    

class Test_LogHelpers(unittest.TestCase):
    def test_mini(self):
        myClass.incrementVar("LogHelpers")

For some reason skipping the init.py (and leaving it blank) and going for the explicit importation worked. It also maintained state - I created a duplicate of the test file and my log output correctly had '3' for the first file to call the helper, and '4' for the second file to call the helper.

Thanks Daniel Roseman for the help and suggestions, they had me look a bit more in the right direction. If you can spot why the previous stuff wasn't working it would be much appreciate just to add to my understanding of this language, but I'm gonna go ahead and mark your answer as 'Answered' since it had some very useful feedback.

like image 999
Daniel Cazan Avatar asked Nov 06 '14 07:11

Daniel Cazan


People also ask

What does undefined at the module level mean?

You do not have permission to delete messages in this group. to. It probably wants you to have a line at the top-level of the module (outside of any function) defining the variable: global_var = None # or some other default value. --Ned.

Why is my global variable undefined?

The reason the first alert is undefined is because you re-declared global as a local variable below it in the function. And in javascript that means from the top of the function it is considered the local variable.

How do I use a global variable in another Python file?

To use global variables between files in Python, we can use the global keyword to define a global variable in a module file. Then we can import the module in another module and reference the global variable directly. We import the settings and subfile modules in main.py . Then we call settings.

What are global variables in Python?

Variables that are created outside of a function (as in all of the examples above) are known as global variables. Global variables can be used by everyone, both inside of functions and outside.


1 Answers

Before I start, note that the PyCharm warnings are not actual Python errors: if you ran your code, you would probably get more useful feedback (remember static analysis of a dynamic language like Python can only get you so far, many things can't be resolved until you actually run the code).

Firstly, it's really not clear why you have nested classes here. The outer class seems completely useless; you should remove it.

The reason for the error message about "self" is that you have defined an instance method, which can only be called on an instance of log. You could make mylogger (absolutely no need for the double-underscore prefix) an instance: mylogger = log() - and then import that, or import the class and instantiate it where it is used.

So in your first snippet, the error message is quite clear: you have not defined mylogger. Using my recommendation above, you can do from helpers import mylogger and then directly call mylogger.classEnter().

Finally, I can't see what that global statement is doing in someFunction. There's no need to declare a name as global unless you plan to reassign it within your scope and have that reassignment reflected in the global scope. You're not doing that here, so no need for global.

By the way, you should also question whether you even need the inner log class. Generally speaking, classes are only useful when you need to store some kind of state in the object. Here, as your docstring says, you have a collection of utility methods. So why put them in a class? Just make them top-level functions inside the logHelpers module (incidentally, Python style prefers lower_case_with_underscore for module names, so it should be "log_helpers.py").

like image 56
Daniel Roseman Avatar answered Sep 18 '22 07:09

Daniel Roseman