Verifying python version in a script is stopped by SyntaxError - python

Using features from newer python versions, e.g. f-string debugging feature: f'{some_var=}', results into a SyntaxError.
Suppose I have a python script which I would like to provide, and the user executes said script with an old python version, he will just get this error. Instead I would like to provide him with some meaningful text, e.g. "Please update python to version >=3.7"
I can solve it with a main file, which checks the version and then imports my script.
Is there a way to achieve this, while still having only a single file script?
Possible approaches:
Check sys.version or platfrom.python_version_tuple
-> Not possible, SyntaxError gets in the way, as python parses whole files
Use eval to determine SyntaxError: -> Not possible for same reasons
try:
a = "hello"
eval("f'{a=}'")
except SyntaxError:
raise ImportError('Update your Python version!!!!')
Can I trick Python somehow to not syntactically check the whole file?
(I could "pack" the whole file into a string, check for the version and then eval the string, but that is not a very clean solution and it is terrible for development)
Edit:
This question is not about "HOW to check for python version". Instead it is about "How to check for python version, before I receive SyntaxError due to new features.

I can think of one potential solution for this, where you wrap your entire script in a triple-quoted string (you'll need to make sure that it doesn't conflict with any triple-quoted strings in your script), and pass that to exec, like so:
import sys
if sys.version_info < (3, 7):
raise ImportError("I need python 3.7 or higher to run!")
exec('''
# your entire script
''')
... at this point I recommend either just using two files, or documenting on your website or wherever what the syntax error means.

Related

Simulate Python interactive mode

Im writing a private online Python interpreter for VK, which would closely simulate IDLE console. Only me and some people in whitelist would be able to use this feature, no unsafe code which can harm my server. But I have a little problem. For example, I send the string with code def foo():, and I dont want to get SyntaxError but continue defining function line by line without writing long strings with use of \n. exec() and eval() doesn't suit me in that case. What should I use to get desired effect? Sorry if duplicate, still dont get it from similar questions.
The Python standard library provides the code and codeop modules to help you with this. The code module just straight-up simulates the standard interactive interpreter:
import code
code.interact()
It also provides a few facilities for more detailed control and customization of how it works.
If you want to build things up from more basic components, the codeop module provides a command compiler that remembers __future__ statements and recognizes incomplete commands:
import codeop
compiler = codeop.CommandCompiler()
try:
codeobject = compiler(some_source_string)
# codeobject is an exec-utable code object if some_source_string was a
# complete command, or None if the command is incomplete.
except (SyntaxError, OverflowError, ValueError):
# If some_source_string is invalid, we end up here.
# OverflowError and ValueError can occur in some cases involving invalid literals.
It boils down to reading input, then
exec <code> in globals,locals
in an infinite loop.
See e.g. IPython.frontend.terminal.console.interactiveshell.TerminalInteractiveSh
ell.mainloop().
Continuation detection is done in inputsplitter.push_accepts_more() by trying ast.parse().
Actually, IPython already has an interactive web console called Jupyter Notebook, so your best bet should be to reuse it.

Code changes from Python 2.6 to 3.x

I am trying to get pywbem working in Python 3.2 (it works fine in 2.6) but the build fails on this part of code in mof_compiler.py:
File "pywbem-0.7.0\mof_compiler.py", line 1341
print s
^
SyntaxError: invalid syntax
It's a macro, defined like this:
def _print_logger(s):
print s
I don't understand why this is invalid, please explain how to do the same in Python 3.2.
Note: I have little or no experience with Python.
PS: I have already done some small changes to the code for 3.2 like
changing
except CIMError, ce:
to
except CIMError as ce:
based on Lennart Regebro's answer here are some other changes I found (placing them here since it may be useful for others).
exec "import %s as lextab" % tabfile -> exec ("import %s as lextab" % tabfile)
raise ValueError,"Expected a string" -> raise ValueError("Expected a string")
That's not a macro, it's a function definition, and in Python 3 the print statement is now a function. So do print(s) instead.
The list of changes between Python 2 and Python 3 is here: http://docs.python.org/release/3.0.1/whatsnew/3.0.html
It's not so easy to read, but I don't know if there is a better one online (although books exist).
If you are going to use Python 3, you would probably do good to get a Python 3 book. There are a couple of them out now. Or at least refer to the Python 3 documentation: http://docs.python.org/release/3.2/ It has a decent tutorial.
One of the most visible changes in python 3 is print is no longer a statement, but is a function, so you have to use parenthesis for calling that function. print(s)
Also, if you have your Python2 code, just use 2to3 which can do a source to source translation of your python2 to python3, which can fix most of the syntax level changes for you like the above problems. 2to3 is installed with python3 binary.
Sorry for answering an old question, but I just recently wanted to get PyWBEM running under Python 3, so I forked it, made the required changes, and removed a Python 2.x dependency (M2Crypto) from it for the 3.x series. Here's the source from GitHub:
https://github.com/deejross/python3-pywbem
Quick note, this supports Python 2.6, 2.7, and 3.4+

How can I add a command to the Python interactive shell?

I'm trying to save myself just a few keystrokes for a command I type fairly regularly in Python.
In my python startup script, I define a function called load which is similar to import, but adds some functionality. It takes a single string:
def load(s):
# Do some stuff
return something
In order to call this function I have to type
>>> load('something')
I would rather be able to simply type:
>>> load something
I am running Python with readline support, so I know there exists some programmability there, but I don't know if this sort of thing is possible using it.
I attempted to get around this by using the InteractivConsole and creating an instance of it in my startup file, like so:
import code, re, traceback
class LoadingInteractiveConsole(code.InteractiveConsole):
def raw_input(self, prompt = ""):
s = raw_input(prompt)
match = re.match('^load\s+(.+)', s)
if match:
module = match.group(1)
try:
load(module)
print "Loaded " + module
except ImportError:
traceback.print_exc()
return ''
else:
return s
console = LoadingInteractiveConsole()
console.interact("")
This works with the caveat that I have to hit Ctrl-D twice to exit the python interpreter: once to get out of my custom console, once to get out of the real one.
Is there a way to do this without writing a custom C program and embedding the interpreter into it?
Edit
Out of channel, I had the suggestion of appending this to the end of my startup file:
import sys
sys.exit()
It works well enough, but I'm still interested in alternative solutions.
You could try ipython - which gives a python shell which does allow many things including automatic parentheses which gives you the function call as you requested.
I think you want the cmd module.
See a tutorial here:
http://wiki.python.org/moin/CmdModule
Hate to answer my own question, but there hasn't been an answer that works for all the versions of Python I use. Aside from the solution I posted in my question edit (which is what I'm now using), here's another:
Edit .bashrc to contain the following lines:
alias python3='python3 ~/py/shellreplace.py'
alias python='python ~/py/shellreplace.py'
alias python27='python27 ~/py/shellreplace.py'
Then simply move all of the LoadingInteractiveConsole code into the file ~/py/shellreplace.py Once the script finishes executing, python will cease executing, and the improved interactive session will be seamless.

Checking Version of Python Interpreter Upon Execution of Script With Invalid Syntax

I have a Python script that uses Python version 2.6 syntax (Except error as value:) which version 2.5 complains about. So in my script I have included some code to check for the Python interpreter version before proceeding so that the user doesn't get hit with a nasty error, however, no matter where I place that code, it doesn't work. Once it hits the strange syntax it throws the syntax error, disregarding any attempts of mine of version checking.
I know I could simply place a try/except block over the area that the SyntaxError occurs and generate the message there but I am wondering if there is a more "elegant" way. As I am not very keen on placing try/except blocks all over my code to address the version issue. I looked into using an __ init__.py file, but the user won't be importing/using my code as a package, so I don't think that route will work, unless I am missing something...
Here is my version checking code:
import sys
def isPythonVersion(version):
if float(sys.version[:3]) >= version:
return True
else:
return False
if not isPythonVersion(2.6):
print "You are running Python version", sys.version[:3], ", version 2.6 or 2.7 is required. Please update. Aborting..."
exit()
Create a wrapper script that checks the version and calls your real script -- this gives you a chance to check the version before the interpreter tries to syntax-check the real script.
Something like this in beginning of code?
import sys
if sys.version_info<(2,6):
raise SystemExit('Sorry, this code need Python 2.6 or higher')
In sys.version_info you will find the version information stored in a tuple:
sys.version_info
(2, 6, 6, 'final', 0)
Now you can compare:
def isPythonVersion(version):
return version >= sys.version_info[0] + sys.version_info[1] / 10.
If speed is not a priority, you can avoid this problem entirely by using sys.exc_info to grab the details of the last exception.

Can you access registers from python functions in vim

It seems vims python sripting is designed to edit buffer and files rather than work nicely with vims registers. You can use some of the vim packages commands to get access to the registers but its not pretty.
My solution for creating a vim function using python that uses a register
is something like this.
function printUnnamedRegister()
python <<EOF
print vim.eval('##')
EOF
endfunction
Setting registers may also be possible using something like
function setUnnamedRegsiter()
python <<EOF
s = "Some \"crazy\" string\nwith interesting characters"
vim.command('let ##="%s"' % myescapefn(s) )
EOF
endfunction
However this feels a bit cumbersome and I'm not sure exactly what myescapefn should be.
So I've never been able to get the setting version to work properly.
So if there's a way to do something more like
function printUnnamedRegister()
python <<EOF
print vim.getRegister('#')
EOF
endfunction
function setUnnamedRegsiter()
python <<EOF
s = "Some \"crazy\" string\nwith interesting characters"
vim.setRegister('#',s)
EOF
endfunction
Or even a nice version of myescapefn I could use then that would be very handy.
UPDATE:
Based on the solution by ZyX I'm using this piece of python
def setRegister(reg, value):
vim.command( "let #%s='%s'" % (reg, value.replace("'","''") ) )
If you use single quotes everything you need is to replace every occurence of single quote with two single quotes.
Something like that:
python import vim, re
python def senclose(str): return "'"+re.sub(re.compile("'"), "''", str)+"'"
python vim.command("let #r="+senclose("string with single 'quotes'"))
Update: this method relies heavily on an (undocumented) feature of the difference between
let abc='string
with newline'
and
execute "let abc='string\nwith newline'"
: while the first fails the second succeeds (and it is not the single example of differences between newline handling in :execute and plain files). On the other hand, eval() is somewhat more expected to handle this since string("string\nwith newline") returns exactly the same thing senclose does, so I write this things now only using vim.eval:
python senclose = lambda str: "'"+str.replace("'", "''")+"'"
python vim.eval("setreg('#r', {0})".format(senclose("string with single 'quotes'")))

Categories

Resources