Why does my mini program work but my main program does not?

I have Python 3.11.9 on Windows 10 Pro. My virtual env is activated. I’m trying to get some system information on my own to practice Python. My mini program works but my main program does not.

This mini program works fine in the debugger pdb.

import inspect
import platform # Core Python library

###########################################################
def printdetail():
    procname = str(inspect.stack()[0][3]) + ":"
    
    #print(f"Architecture: {platform.architecture()}")
    print(f"Network name/Computer name: {platform.node()}")
    print(f"OS: {platform.platform()}")
    print(f"Processor: {platform.processor()}")
    
###########################################################
# Main program

printdetail()

But when I run this in my main program, which has some class definitions, and other code, here’s what I get. Here’s part of the function to get the same information.

def printdetail(options):
    procname = str(inspect.stack()[0][3]) + ":"
    
    t = platform.platform() # Returns string
    print(f"OS: {t}")
    t = platform.release() # Returns string.
    t = platform.system() # Returns string.
    t = platform.version() # Returns string.

Error in the debugger:

-> t = platform.platform() # Returns string
(Pdb) t = str(platform.platform())
*** AttributeError: 'str' object has no attribute 'platform'
(Pdb) p platform.platform()
*** AttributeError: 'str' object has no attribute 'platform'
(Pdb) p str(platform.platform())
*** AttributeError: 'str' object has no attribute 'platform'

If I do this, it works.

-> import platform # Core Python library
(Pdb)
> c:\users\USER\onedrive - COMPANY\documents\pythonprojects\sysinfo\sysinfo2.py(20)<module>()
-> import psutil
(Pdb) p platform.platform()
'Windows-10-10.0.19045-SP0'
(Pdb)

The platform variable seems to be getting reset somewhere but where?

I’m baffled.

Thank you!

1 Like

Hi,

I ran the code snippet on Python v3.13 and get no such errors … works fine.

I have two questions:

  1. Why does the function have the parameter options in its header when it is not used in
    the body?
  2. Why do you keep overriding the value of t without first using the results?

By the way, in order for it to work, I had to enter an argument for the options parameter otherwise it would not work.

TypeError: printdetail() missing 1 required positional argument: 'options'

Do you assign anything to the platform name anywhere? Do you modify frames, globals, locals or builtins anywhere? Do you fiddle with sys.modules anywhere? Any of these could explain the error.

I fixed it. For some reason if I do the import in the actual function, it works. Perhaps importing the module initializes a class instance variable which is needed.

def printdetail(options):
    procname = str(inspect.stack()[0][3]) + ":"
    import platform
    import cpuinfo
    import psutil
    import wmi
  1. options is a variable I use to pass values to functions. It is not being used in the code snippet I provided, and that is an oversight on my part.
  2. The t variable was used to get various values which I could look at in the debugger to see what they contain.

This makes the platform name to be locally defined as the module. Somehow you had it globally set to a string. Would you still like to figure out why?

Yes please. The documentation for platform.platform() says it returns a string. But that wasn’t working.

That is very strange. This should not be an issue. I imported platform at the top of the script and get no such error. Seems like something else is at play.

Can you share more of your code? Does it do any of the things I mentioned in

from the error message it seems that the main program sees platform as a string.
Are you sure there isn’t a variable named platform in scope ?

Since the variable is declared in the global scope, this function will work:

def find_variable_definition(variable_name):
    # Read the current module file and search for the variable assignment
    with open(__file__, 'r') as file:
        lines = file.readlines()

    # Find the line number where the variable is defined
    for i, line in enumerate(lines, 1):
        # Strip whitespace and check if the line starts with the variable name
        stripped_line = line.strip()
        if stripped_line.startswith(f"{variable_name}"):
            print(f"Variable '{variable_name}' is defined at line {i}: {line.strip()}")
            return i, line.strip()

    # If the variable is not found, inform the user
    print(f"Variable '{variable_name}' not found.")
    return None, None

# Test the function
a = 42
b = "Hello, world!"
find_variable_definition("a")  # This will find the definition of 'a'
find_variable_definition("b")  # This will find the definition of 'b'

Yes I do add a path to sys.path(). Here’s my full code that has the trouble. The filename is sysinfo2.py.

NOTE:

  1. Pycharm is not used for this. I simply run the code in cmd.exe with python sysinfo2.py -summary. For debugging I use python -m pdb sysinfo2.py -summary
  2. I pass in the variable options as a habit to functions. It is not actually used in the functions in this program.
r'''
Get hardware and OS info for Windows or Linux systems.
Created: 5/17/24

Install these: python -m pip install psutil py-cpuinfo wmi
"wmi" is only for windows systems. It does not work with Linux. 

import inspect
    procname = str(inspect.stack()[0][3]) + ":"

===Documentation for modules

psutil: https://pypi.org/project/psutil/
py-cupinfo: https://github.com/workhorsy/py-cpuinfo https://pypi.org/project/py-cpuinfo/
wmi: https://pypi.org/project/WMI/

'''
import inspect
import platform # Core Python library
import psutil
import cpuinfo
import wmi # For windows only, will not work on Linux.
import sys


# Add my personal utilities library. 
sys.path.append(r"c:/users/USER/Pycharmprojects/ggutil2024") # Required to use ggutil
try:
    from ggutil import * # Custom utils. Must go last.
except ImportError as e:
    print('ERROR: could not import ggutil.',e)
    sys.exit()
    
SUBVER=1
VER = "2024-11-07." + str(f"{SUBVER:02d}")
APPTITLE = "Show hardware and OS info."
#####################################################
# Classes
# Option variables go here.
class clsOptions: # Command line options
    r'''
    This holds all command line options.
    .overwrite = set to True to overwrite error logs and output files during testing. Source is -overwrite option.
    '''
    def __init__(self):
        r'''These are class variables we need to use 
        with initial values.'''
        self.adminemail = 'foo@bar.com'
        self.datetoday = getdate_YYYY_MM_DD() # Used with filenames.
        self.debug = False # Should we delete inventory?
        self.debugprint = False # True to show debug messages.
        self.dirsep = os.sep
        self.mydate = getdate_YYYY_MM_DD() # Used for filenames
        self.mydatetime = getdatetime_fn() # For filenames.
        self.overwrite = False # True to overwrite output files, for testing.
        self.progpath = __file__
        self.progdir = os.path.dirname(__file__)
        self.starttime = 0
        self.starttime_human = ''
        self.summary = False
    def __repr__(self):
        r'''Usage: in debugger p(options)
        Print boolean variables first, then strings, then lists.
        '''
        procname = str(inspect.stack()[0][3]) + ":"
        s = f"{procname} This holds command line options.\n"
        s = s + f"adminemail={self.adminemail}\n"
        s = s + f"datetoday={self.datetoday}\n"
        s = s + f"debug={self.debug}\n"
        s = s + f"debugprint={self.debugprint}\n"
        s = s + f"dirsep={self.dirsep}\n"
        s = s + f"mydate={self.mydate}\n"
        s = s + f"mydatetime={self.mydatetime}\n"
        s = s + f"overwrite={self.overwrite}\n"
        s = s + f"progpath={self.progpath}\n"
        s = s + f"progdir={self.progdir}\n"
        s = s + f"starttime={self.starttime}\n"
        s = s + f"starttime_human={self.starttime_human}\n"
        s = s + f"summary={self.summary}\n"
        return s
        
    def __str__(self):
        r'''Used with print(options)'''
        s = f"This is __str__\n"
        __repr__(self)
        return s        

    def __sizeof__(self):
        return "The sizeof method no worky."
        
options = clsOptions()
options.starttime = time.time()
options.donedir = os.path.join(options.progdir, r'done')
options.logfn = os.path.join(options.progdir, r'log.txt')

#################################################
#################################################
def argparseit(options):
    procname = str(inspect.stack()[0][3]) + ":"
    
    errorflag = False
    maxargpos = len(sys.argv)
    for i in range(1,maxargpos): 
        myarg = sys.argv[i] 
        alist = myarg.split(':')
        alist[0] = alist[0].upper()
        
        if myarg[0]=='-': 
            if alist[0] == '-DEBUG':
                options.debug = True
            elif alist[0] == '-DETAIL':
                options.summary = False
            elif alist[0] == '-OVERWRITE':
                options.overwrite = True
            elif alist[0] == '-SUMMARY':
                options.summary = True
            elif alist[0] == '-V': # Print version info.
                # print(f"{APPTITLE} v{VER}")
                print(f"{MODULEVER}")
                sys.exit()
            else: 
                print(f"{procname} ERROR: Invalid arg %s" % (myarg))
                errorflag = True
             
        else: 
            print(f"{procname} ERROR: Looks like a bad argument: %s" % (myarg))
            sys.exit()
    if errorflag: # Done with argparseit()
        print(f"{procname} Please fix errors and try again.")
        sys.exit()

    
#################################################
def printdetail(options):
    procname = str(inspect.stack()[0][3]) + ":"
    import platform
    import cpuinfo
    import psutil
    import wmi
    
    t = platform.platform() # Returns string
    print(f"OS: {t}")
    # t = platform.release() # Returns string.
    # t = platform.system() # Returns string.
    # t = platform.version() # Returns string.
    # Error below: AttributeError: 'str' object has no attribute 'node'. Did you mean: 'encode'?
    print(f"Network name/Computer name:", platform.node())
    print(f"Processor:", platform.processor()) # Returns string.
    t = platform.machine() # Returns string or empty string. Ex: "AMD64"
    
    # Get python version.
    ver = sys.version
    print(f"Python version: {ver}")

    cpuinfo = cpuinfo.get_cpu_info() # Returns a dictionary
    #print(cpuinfo.keys()) # To print keys. 
    print(f"Full CPU name: {cpuinfo['brand_raw']}")
    print(f"CPU speed: {cpuinfo['hz_actual_friendly']}")
    t = cpuinfo['hz_advertised_friendly']
    print(f"CPU speed advertised: {t}")
    print(f"Total RAM: {human_storage(psutil.virtual_memory().total)}")
    d = cpuinfo['l2_cache_size'] / 1024
    print(f"L2 cache: {d} MB")
    flags = cpuinfo['flags'] # List of strings.
    print(f"CPU flags: {flags}")

    print("")
    pc = wmi.WMI() # For Windows systems only. 
    # print(os_info.status)
    # os_info = pc.Win32_OperatingSystem()[0] # Very detailed OS info.
    # print(f"OS Info: {os_info}")
    # print(f"wmi CPU info: {pc.Win32_Processor()[0]}")
    # Get GPU/video card info
    vcard = pc.Win32_VideoController()[0] # Do print(vcard)
    print(f"Video Card: {pc.Win32_VideoController()[0].Name}")
    print(f"Video Card Driver Version: {pc.Win32_VideoController()[0].DriverVersion}")
    t = pc.Win32_VideoController()[0].AdapterRAM
    if t < 0: 
        t = t * -1
    t = human_storage(t)
    print(f"Video Card VRAM: {t}")
    vres = pc.Win32_VideoController()[0].CurrentVerticalResolution
    hres = pc.Win32_VideoController()[0].CurrentHorizontalResolution
    print(f"Video resolution: {hres} x {vres}")
    
#################################################
def printsummary(options):
    procname = str(inspect.stack()[0][3]) + ":"
    import platform
    import cpuinfo
    import psutil
    import wmi
    
    # print(f"Architecture: {platform.architecture()}")
    print(f"Network name/Computer name: {platform.node()}")
    print(f"OS: {platform.platform()}")
    print(f"Processor: {platform.processor()}")
    # t = platform.system()
    
    # Get python version.
    ver = sys.version
    print(f"Python version: {ver}")
    
    cpuinfo = cpuinfo.get_cpu_info() # Returns a dictionary
    #print(cpuinfo.keys()) # To print keys. 
    print(f"Full CPU name: {cpuinfo['brand_raw']}")
    print(f"CPU speed: {cpuinfo['hz_actual_friendly']}")
    print(f"CPU speed advertised: {cpuinfo['hz_advertised_friendly']}")
    print(f"Total RAM: {human_storage(psutil.virtual_memory().total)}")
    print(f"CPU Architecture: {cpuinfo['arch']}")

    print("")
    pc = wmi.WMI() # For Windows systems only. 
    # print(os_info.status)
    # os_info = pc.Win32_OperatingSystem()[0] # Very detailed OS info.
    # print(f"OS Info: {os_info}")
    # print(f"wmi CPU info: {pc.Win32_Processor()[0]}")
    # Get GPU/video card info
    vcard = pc.Win32_VideoController()[0] # Do print(vcard)
    print(f"Video Card: {pc.Win32_VideoController()[0].Name}")
    print(f"Video Card Driver Version: {pc.Win32_VideoController()[0].DriverVersion}")
    vram = pc.Win32_VideoController()[0].AdapterRAM
    tvram = vram
    if vram < 0: 
        vram = vram * -1
    vram = human_storage(vram)
    print(f"Video Card VRAM: {vram} ({tvram})")
    vres = pc.Win32_VideoController()[0].CurrentVerticalResolution
    hres = pc.Win32_VideoController()[0].CurrentHorizontalResolution
    print(f"Video resolution: {hres} x {vres}")

    
#################################################
#################################################
# Main program
helpstr = f'''Syntax: Python {sys.argv[0]} OK
{APPTITLE} v{VER}

To send output to a file do: python {sys.argv[0]} OK > myfile.txt

OPTIONS:
-detail: print detailed info about OS and hardware. 
-summary: Print a summary of features, ignoring features most people don't want to see. 
'''

if len(sys.argv)<=1:
    print(helpstr) # Print help screen and exit.
    sys.exit() # Exit program
print("\n=====================================")
print(f"{APPTITLE} v{VER}\n")
argparseit(options)

if options.summary: 
    printsummary(options)
else:
    printdetail(options)

Avoid importing with a wildcard (e.g., import *); it is considered bad practice.

Is there a platform variable defined in your ggutil library?

3 Likes

I’m guessing you have a variable named platform in the ggutil module, can you check?

4 Likes

Well crap, you’re right! In ggutil.py it is

from sys import platform

Problem solved. I thought that variable would be scoped to that ggutil.py file but it isn’t?

The ggutil.py is a single file with 60+ functions in it that I commonly use in my programs. Whenever I write a program I never know how many functions from ggutil.py I will use.

Apparently I do not understand all the issues of scoping yet. So if I do this import on my own utilities file called ggutil.py, all those import are scoped to the main program that imports ggutil.py? Or am I missing something?

When I write a new program I don’t know how many functions I will need in that single ggutil.py file. I may need 10 or 20 of them. There must be 60+ functions in that single .py file.

1 Like

When you do a star import, it grabs everything from that file. That’s why star imports are usually a bad idea.

1 Like

If you’re doing from ggutil import * to save keystrokes you could always give it a smaller alias:

import ggutil as gg

gg.whatever()

It would also make it easier for someone reading your code to see where the functions come from.

1 Like