Python (2.7): Simple version checker - python

A preface: I'm a beginner at Python. I've tried guides for learning but I am awful at learning like that, so I'm trying to make a super simple update checker to get started that I slowly build upon. I've grabbed some code I found on here and modified it a bit, and alas, it doesn't work. It reads the local and external .txt files and prints their output (just to check that it's reading them correctly). It then fails at the if/elif/elif/else statement thing in some way, so some help there would be great!
It's currently telling me "NameError: global name 'i' is not defined" however I've gone through several different errors at this point and am really just looking for a solution, and from there I can work backwards. Thanks!
import Tkinter
import urllib
import time
print "test"
#previously self within the brackets
def updateCheck():
update = False
updateWindow = Tkinter.Toplevel()
updateWindow.title(string="Update Checker")
updateWindow.resizable(False, False)
#gets local version (file currently says "1.0")
localSource = open('version.txt', 'r')
localContents = localSource.read()
print "local version = " + localContents
#gets server version (file currently says "1.1")
serverSource = urllib.urlopen("http://raw.github.com/SamHH/ccr-version/master/version.txt")
serverContents = serverSource.read()
print "server version = " + serverContents
#checks for updates by comparing the above two -- doesn't work
if serverContents[i] > localContents[i]:
dataLabel = Tkinter.Label(updateWindow,text="\n\nThere is an update available.\n\n")
dataLabel.pack()
#need a way of opening a .url file in the same folder here, if possible
elif serverContents[i] < localContents[i]:
dataLabel = Tkinter.Label(updateWindow,text="\n\nYour installation appears to be broken.\n\n")
dataLabel.pack()
#need a way of opening a .url file in the same folder here, if possible, again
elif serverContents[i] == localContents[i]:
versionLabel = Tkinter.Label(updateWindow,text="\n\nYou are already running the most up to date version.\n\n")
versionLabel.pack()
#need a way of opening a .exe file in the same folder this time, if possible
else:
versionLabel = Tkinter.Label(updateWindow,text="\n\nYour install is corrupted. Doh!\n\n")
versionLabel.pack()
updateCheck()

if serverContents[i] > localContents[i]:
Notice that you never initialized i to a default value. It is looking up in your code to see if you had defined and set it outside the function (which you did not).
Put in a loop
for i in range(len(serverContents)):
You should also check that both lists are the same size or you will get an error when you try to index past the end.
Note that this assumes that the serverContents and localContents are both lists each of whose elements is a value to be compared. If the contents are text strings. then you will be looping over each character in the string. If that is what you want, you do not need to do it
f = '1.1a'
g = '1.1a'
f == g # shows True
f is g # shows False
This will mean that '1.1a' and '01.1a' will show different
However, this will allow for the case where version number is not totally numeric, which is a requirement if you use float(serverContents).

If both local and remove 'files' contain just a float, read one line from each and turn that into a float() so you can compare:
try:
localSource = open('version.txt', 'r')
localContents = float(localSource.readline())
except (IOError, ValueError):
versionLabel = Tkinter.Label(updateWindow,text="\n\nYour install is corrupted. Doh!\n\n")
versionLabel.pack()
return
serverSource = urllib.urlopen("http://raw.github.com/SamHH/ccr-version/master/version.txt")
serverContents = float(serverSource.readline())
You then use the localContents and serverContents names to compare:
if serverContents > localContents:
# etc.

Related

How to use a variable dynamically from an imported module in python 3.8

I currently have the following file load.py which contains:
readText1 = "test1"
name1 = "test1"
readText1 = "test2"
name1 = "test2"
Please note that the number will change frequently. Sometimes there might be 2, sometimes 20 etc.
I need to do something with this data and then save it individually.
In my file I import load like so:
from do.load import *
#where do is a directory
I then create a variable to know how many items are in the file (which I know)
values = range(2)
I then attempt to loop and use each "variable by name" like so:
for x in values:
x = x + 1
textData = readText + x
nameSave = name + x
Notice I try to create a textData variable with the readText but this won't work since readText isn't actually a variable. It errors. This was my attempt but it's obviously not going to work. What I need to do is loop over each item in that file and then use it's individual variable data. How can I accomplish that?
This is a common anti-pattern that you are stepping into. Every time you think "I'll dynamically reference a variable to solve this problem" or "Variable number of variables!" think instead "Dictionary".
load.py can instead contain a dictionary:
load_dict = {'readText1':'test1','name1':'test1','readText2':'test2','name2':'test2'}
You can make that as big or small as you want.
Then in your other script
from do.load import *
#print out everything in the dictionary
for k,v in load_dict.items():
print(k,v)
#create a variable and assign a value from the dictionary, dynamically even
for x in range(2):
text_data = load_dict['readText' + x]
print(text_data)
x+=1
This should allow you to solve whatever you are trying to solve and won't cause you the pain you will find if you continue down your current path.
If you are trying to access the variables in the module you've imported, you can use dir.
loader.py
import load
values = dir(load) # All the values in load.py
# to get how many they are
num_vars = len([var for var in module_vars if not var.startswith("__")])
print(num_vars)
# to get their names
var_names = [var for var in module_vars if not var.startswith("__")]
print(var_names)
# to get their values
var_values = [globals()[f"module.{var}"] for var in var_names]
print(var_values)
However, it is unsafe as it may introduce security vulnerabilities to your code. It is also slower. You can use data structures as JNevil has said here, here
The file load.py will load only the last variable "readText1" and "name1".
To do what you are asking for, you have to open load.py file as a text file and then iterate over each line to get 2 variables ("readText1" and "name1") for each iteration.

How to write a python function inside SPSS that creates new variables and appends them to the SPSS dataset?

I have recently started writing small Python program blocks in SPSS.
In my current dataset I coalesce two variables like this:
BEGIN PROGRAM PYTHON3.
import spss, spssdata
data = spssdata.Spssdata(indexes = ('var1', 'var2'), accessType = 'w')
data.append(spssdata.vdef("newvar"))
data.commitdict()
for row in data:
if row.var1== None:
data.setvalue("newvar", row.var2)
else:
data.setvalue("newvar", row.var1)
data.CClose()
END PROGRAM.
The above program works perfectly well and now I would like to create a function as I need to coalesce many more variables.
The best solution I came up with so far is this one:
BEGIN PROGRAM PYTHON3.
import spss, spssdata
def coalesce(var1, var2, newvar):
data = spssdata.Spssdata(indexes = (var1, var2), accessType = 'w')
data.append(spssdata.vdef(newvar))
data.commitdict()
for row in data:
if row.var1 == None:
data.setvalue("newvar", row.var1)
else:
data.setvalue("newvar", row.var2)
data.CClose()
END PROGRAM.
However, I get the following error message when trying to run it on my variables:
BEGIN PROGRAM PYTHON3.
coalesce('Statements1', 'Statements2', 'Statements12')
END PROGRAM.
Warning: An open Cursor was detected while exiting a program block. The Cursor has been closed.
Traceback (most recent call last):
File "<string>", line 2, in <module>
File "<string>", line 8, in coalesce
AttributeError: 'namedTuple' object has no attribute 'var1'
I suppose that the problem is that I am passing the tuple name rather than the tuple in line 8 but I don't know how to fix that. Already tried different solutions, e.g., tried to pass the variable without quotes in the function but this did not work either. Also, tried to do something like row[var1] in line 8 but it seems that spssdata is not able to work with that.
Anyone who can help me with this problem? Thank you!
In the meantime I came up with a solution myself.
Maybe not the most elegant one but it works fine.
In the "set.value" part in which the error ocurred I am now taking advantage of the fact that the variable row is a tuple, which can also be accessed by index. So, row[0] refers to the values of var1 and row[1] the values of var2.
Furthermore, I am using SPSS to rename the new variable after creating it.
BEGIN PROGRAM PYTHON3.
import spss, spssdata, spssaux
def coalesce(var1, var2, nameAggregate):
#get Variable dictionary info in order to get variable label
sDict = spssaux.VariableDict(caseless = True)
var1Label = sDict[var1].VariableLabel
#create data cursor
data = spssdata.Spssdata(indexes = (var1, var2), accessType = 'w')
data.append(spssdata.vdef("newvar"))
data.commitdict()
for row in data:
if row[0] == None:
data.setvalue("newvar", row[1])
else:
data.setvalue("newvar", row[0])
data.CClose()
#copy label and add "COALESCED" as a prefix
spss.Submit(r"""
APPLY DICTIONARY FROM *
/source variables = %(var1)s
/target variables = newvar
/VARINFO MISSING VALLABELS=REPLACE.
VARIABLE LABELS newvar "COALESCED: %(var1)s & %(var2)s %(var1Label)s".
RENAME VARIABLES (newvar = %(nameAggregate)s).
"""%locals())
END PROGRAM.
Example use:
BEGIN PROGRAM PYTHON3.
coalesce('Statement1', 'StatementsNot1', 'AggregateStatement1')
END PROGRAM.
I further recommend this resource for python programming inside SPSS https://www.spsstools.net/en/documents/76/SPSS-Programming-and-Data-Management-3rd-Edition.pdf

How to generate auto increment in a folder in python?

I am new to Python. Anyone help with how to generate auto-increment like B00001, B00002, B00003...... which can autosave the excel file name with a button in a specific folder.
I have tried with
global numXlsx
numXlsx = 1
wb.save(f'invoice/B{numXlsx}.xlsx')
numXlsx += 1
But when I click the button for few times with different data, it still keeps overwriting the B1.xlsx file. Anyone help with this :)
It sounds like the biggest problem you're having is that each button click is re-starting the execution of your python script, so using a global variable won't work since that doesn't persist across executions. In this case, I'd suggest using something like the pickle module to store and reload your counter value each time you execute your script. Using that module, your solution could look something like this:
import pickle
from pathlib import Path
# creates file if it doesn't exist
myfile = Path("save.p")
myfile.touch(exist_ok=True)
persisted = {}
with (open(myfile, "rb")) as f:
try:
persisted = pickle.load(f)
except EOFError:
print("file was empty, nothing to load")
# use get() to avoid KeyError if key doesn't exist
if persisted.get('counter') is None:
persisted['counter'] = 1
wb.save(f"invoice/B{persisted.get('counter')}.xlsx")
persisted['counter'] += 1
# save everything back into the same file to be used next execution
pickle.dump(persisted, open(myfile, "wb"))
BONUS: If you want the count to be padded with zeros in the filename, use persisted.get('counter'):05d in the curly brackets when saving the file. The 5 indicates you want the resulting value to be at least 5 characters long, so for example 2 would become 00002 and 111 would become 00111.
You can try using a global variable and incrementing it everytime.
Try with something like:
(inizialize it to 0)
global numXlsx # this is like your counter variable)
wb.save(f'folder/B{numXlsx}.xlsx')
numXlsx += 1 # Incrementing the variable so it does not overwrite the file as your code is doing
Have a nice day!

(Python) Hash function returns different values depending on how the script is called (cmd vs IDE)

I have a script that scrapes cell value data from an excel workbook into nested tuples. Then it returns the hash of that tuple. I can run it in my IDE (Spyder 4.0.1, Python 3.7), or I can call the function using command line.
The problem is that that hash is a different number depending on how I call it. This should not be the case, as it should be pulling the same exact data from the same exact excel workbook, and then using the same exact hash function. I already tried some debugging but I'm running out of ideas. Thoughts?
The relevant code:
import extract #my own code, which contains open_excel()
import sys
#This function takes nested lists and turns them into nested tuples.
def list2tuple(l):
lcopy = []
for item in l:
if type(item) == list:
lcopy.append(list2tuple(item))
else:
lcopy.append(item)
return tuple(lcopy)
def hashxl(filename):
filetype = filename[filename.index('.')+1:]
if filetype in ['xlsx','xlsb']:
f = extract.open_excel(filename) #This should be a list of lists of lists of data (sheets, rows, columns of excel data)
h = hash(list2tuple(f))
return h
if sys.argv[1] == 'hash':
print(hashxl(sys.argv[2])
When I run
python thiscodefile.py hash testfile.xlsb
in command line I get -3482465542484766986. When I run
hashxl("testfile.xlsb")
in the Spyder IDE I get 6187680721660987353.
Yes that's normal because python uses a random hash seed to solve such problems, vulnerability disclosure. To solve your problem you can set a fixed seed using the PYTHONHASHSEED.
The best and easiest way to do it is:
import os
import sys
hashseed = os.getenv('PYTHONHASHSEED')
if not hashseed:
os.environ['PYTHONHASHSEED'] = '0'
os.execv(sys.executable, [sys.executable] + sys.argv)
[your code here]

Detect Window's Default Media Player

I'm trying to detect Window's default media player path so that I can access it from my Python/wxPython program. My specific need is to make a list of all media files and play it using the player.
Based on the comments above, it looks like you decided to go in another direction with this. Your question made me curious though so I did some hunting around anyway.
File associations are stored in the Windows Registry. The way to access Windows Registry information via python is to use the _winreg module (available in versions 2.0 and later). Individual file association information for the current user will be stored at subkeys named as follows:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.wmv\UserChoices
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.mpeg\UserChoices
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.avi\UserChoices
etc, etc for any specific file format you are looking for.
Here is a small example script I've written to access this information and store it as a list:
import _winreg as wr
# Just picked three formats - feel free to substitute or extend as needed
videoFormats = ('.wmv', '.avi', '.mpeg')
#Results written to this list
userOpenPref = []
for i in videoFormats:
subkey = ("Software\\Microsoft\\Windows\\CurrentVersion" +
"\\Explorer\\FileExts\\" + i + "\\UserChoice")
explorer = wr.OpenKey(wr.HKEY_CURRENT_USER, subkey)
try:
i = 0
while 1:
# _winreg.EnumValue() returns a tuple:
# (subkey_name, subkey_value, subkey_type)
# For this key, these tuples would look like this:
# ('ProgID', '<default-program>.AssocFile.<file-type>', 1).
# We are interested only in the value at index 1 here
userOpenPref.append(wr.EnumValue(explorer, i)[1])
i += 1
except WindowsError:
print
explorer.Close()
print userOpenPref
Output:
[u'WMP11.AssocFile.WMV', u'WMP11.AssocFile.avi', u'WMP11.AssocFile.MPEG']
with WMP11 = Windows Media Player 11
Hope this was helpful.
Sources:
python docs, effbot tutorial

Categories

Resources