In Python, what do you do when you write 100 lines of code and forget to add a bunch of loop statements somewhere?
I mean, if you add a while statement somewhere, you've to now indent all the lines below it. It's not like you can just put braces and be done with it. Go to every single line and add tabs/spaces. What if you were adding nested loops/if/then statements to existing code?
Am I missing some shortcut?
I think every serious editor or IDE supports the option to select multiple lines and press tab to indent or Shift-Tab to unindent all that lines.
in IDLE, the standard python IDE, select the code, go on 'format' and you can chooose indent region, dedent region and so on
You have to use an editor command to re-indent.
Keep in mind: Beautiful is better than ugly.
... and the rest of "The Zen of Python, by Tim Peters"
# python -c "import this"
edit: rewrote to accomodate fileinput's "eccentricities"*
def indent_code(filename, startline, endline):
from fileinput import input
from itertools import izip, count
all_remaining = count()
def print_lines(lines, prefix='', range=all_remaining):
for _, line in izip(range, lines):
print prefix + line,
lines = input(filename, inplace=1)
print_lines(lines, range=xrange(1, startline)) # 1-based line numbers
print_lines(lines, ' ', xrange(startline, endline + 1)) # inclusive
print_lines(lines)
def main():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('filename')
parser.add_argument('startline', type=int)
parser.add_argument('endline', type=int)
ns = parser.parse_args()
indent_code(ns.filename, ns.startline, ns.endline)
if __name__ == '__main__':
main()
Well, either that or >}.
*: I originally wrote this using a nice, concise combination of stdout.writelines and some generator expressions. Unfortunately, that code didn't work. The iterator returned by fileinput.input() doesn't actually open a file until you call its next method. It works its sketchy output-redirection magic on sys.stdout at the same time. This means that if you call sys.stdout.writelines and pass it the fileinput.input iterator, your call, and the output, goes to the original standard out rather than the one remapped by fileinput to the file "currently" being processed. So you end up with the lines that are supposed to replace the contents of the file being instead just printed to the terminal.
It's possible to work around this issue by calling next on the fileinput iterator before calling stdout.writelines, but this causes other problems: reaching the end of the input file causes its handle to be closed from the iterator's next method when called within file.writelines. Under Python 2.6, this segfaults because there's no check made (in the C code which implements writelines) to see if the file is still open, and the file handle non-zero, after getting the next value from the iterator. I think under 2.7 it just throws an exception, so this strategy might work there.
The above code actually does test correctly.
textmate (and maybe e?): select then apple-]
bbedit:
also select then apple-]
emacs:
select then M-x 'indent-region'
bpython: don't know, autoindenting is
so easy in bpython, you'd have to
work to break it
xcode: don't do python in xcode
that's generally all I need to know. also yeah it's easy to slap a brace above or below a poorly indented block, but you know it's just going to confuse the shit out of you a week later when you haven't been staring at it for like a day. srsly u guys.
Related
I completely understand that I should have written the script right the first time, but the fact is I have a script that generates a data file based upon two values passed to it from the command line- like this:
[sinux1~]: ./sim_gen.py 100 .3
I need to call this script from within another script, iterating over a range of values. I searched around and after navigating through all of the "you shouldn't," I tried :
exec(open("./sim_gen.py 100 .3").read())
And this doesn't seem to work.
Help?
Let's break this down into pieces:
exec(open("./sim_gen.py 100 .3").read())
This is equivalent to:
f = open("./sim_gen.py 100 .3")
contents = f.read()
exec(contents)
That open is the same open you use for, say, reading a text file or a CSV. You're asking for a file named "sim_gen.py 100 .3" in the current directory. Do you have one? Of course not. So the open fails.
The best solution is, as you already know, to rewrite sim_gen.py so that you can import it and call a function and pass the arguments to it.
Failing that, the cleanest answer is probably to just run the Python script as a subprocess:
import subprocess
import sys
subprocess.run([sys.executable, "./sim_gen.py", "100", ".3"])
Notice that this is effectively the same thing you're doing when you run the script from your shell, so if it was OK there, it's almost surely OK here.
If you really need to exec for some reason, you will need to do something really hacky, and temporarily change argv for that script's code:
import sys
_argv = sys.argv
try:
sys.argv = ["./sim_gen.py", "100", ".3"]
with open("./sim_gen.py 100 .3"):
exec(f.read())
finally:
sys.argv = _argv
Although really, unless the point of running this is to silently modify your own module's globals or the like, you (a) almost certainly don't really need exec, and (b) want to pass an explicit globals argument even if you do really need it.
I added this line of Code:
tweet_string = 'Starting activity for insta'
os.system("python3 tweet.py tweet_string")
As you can see the tweet script uses the first argument as tweet context. Unfortunately I don't know how to use the variable correct... Can you assist me here?
Of course, don't pass your variable name in the literal string...
Just adding out of quotes doesn't cut it because of the spaces. You have to protect with quotes.
it should be:
os.system('python3 tweet.py "{}"'.format(tweet_string))
(better, but if there's a quote in tweet_string you'll have an issue again)
Anyway: don't use os.system it's deprecated. This is better and handles quoting automatically:
import subprocess
subprocess.check_call(["python3","tweet.py",tweet_string])
(Python 3.5 added a unified subprocess.run method which can check return code or not, redirect output in a variable or not, which is the recommended approach to run a subprocess if you don't need to be compatible with previous versions)
Of course, always ask yourself the question when running a python subprocess inside a python module: wouldn't it be easier to import the module and call a function?
In general
('python3 tweet.py "' + tweet_string + '"')
If you're using python 3.6
f('python3 tweet.py "{tweet_string}"')
I am trying to use Python to run an executable (Windows 7) with parameters. I have been able to make the program run, but the amount of parameters I can use that will prove the Python script worked with parameters is limited. The best one is formatted like so:
-debugoutput debug.txt
I have tested this using a windows shortcut with an edited target and it works, it creates a debug output in the program directory.
Here is the code I am using:
import subprocess
args = [r"C:\Users\MyName\LevelEditor\LevelEditor.exe", "-debugoutput debug.txt"]
subprocess.call(args)
This does run the program, but the debug output is not created. I have tried putting an "r" in front of the parameter but this made no difference. I assume it is a simple formatting error but I can't find any examples to learn from that are doing the same thing.
UPDATE:
Thanks for the answers everyone, all the same, simple formatting error indeed.
In-code definition results in invocation of shell command line:
C:\Users\MyName\LevelEditor\LevelEditor.exe "-debugoutput debug.txt"
As you can see, by merging -debugoutput debug.txt to single list element, you explicitly stated that space between them shouldn't be parsed as command line argument separator.
To achieve expected behavior put file name string as separate element to argument list.
[r"C:\Users\MyName\LevelEditor\LevelEditor.exe", "-debugoutput", "debug.txt"]
As far as I know you need to split the arguments by the space, so your args would look like:
args = [r"C:\Users\MyName\LevelEditor\LevelEditor.exe", "-debugoutput", "debug.txt"]
Does that work?
I do not know if it works, but
import subprocess
args = [r"C:\Users\MyName\LevelEditor\LevelEditor.exe", "-debugoutput", "debug.txt"]
subprocess.run(args)
Following the docs
First of all, I want to apologize for even trying to do this. I know that it's not recommended in any way. However, external constraints leave me little other choice than to go this path.
I have a piece of python code that lies on a read-only filesystem. I cannot move it. I cannot modify it. It has an inconsistent use of tabs and spaces. And I need this code to be importable with the -tt option.
Is there any way to ignore the -tt option for a specific import statement, a specific code section, or a certain application altogether?
I fully admit that this is a horrible, horrible solution. I await the downvotes:
dodgymodule.py:
def somefunc():
print("This is indented using 4 spaces")
print("This is indented using a tab")
main python script, which uses autopep8 to fix the code and import the resulting string instead:
import autopep8
import imp
try:
import dodgymodule
except TabError as e:
with open(e.filename, 'r') as f:
new_module_contents = autopep8.fix_code(f.read())
dodgymodule = imp.new_module('dodgymodule')
exec(new_module_contents, dodgymodule.__dict__)
dodgymodule.somefunc()
python3 -tt script.py prints out the lines, as hoped.
I am writing a Python interpreter and want to redirect the function's return values to stdout, like the Python Interpreter in Interactive Mode. Within this mode, when the user calls a function, its return value is printed on the screen. The same occurs with expressions.
E.g.
>>> foo()
'Foo return value'
>>> 2+4
6
>>> print('Hello!')
'Hello!'
Changing the sys.stdout only affects the print function. How do I redirect the other expressions to stdout?
Thank you
First, the interactive mode does not print the return value from any function called. Instead, it prints the result of whatever expression the user typed in. If that's not a function call, it still gets printed. If it has 3 function calls in it, it still prints one result, not 3 lines. And so on.
So, trying to redirect function return values to stdout is the wrong thing to do.
What the interactive interpreter does is something sort of like this:
line = raw_input(sys.ps1)
_ = eval(line)
if _ is not None:
print repr(_)
(You may notice that you can change sys.ps1 from the interactive prompt to change what the prompt looks like, access _ to get the last value, etc.)
However, that's not what it really does. And that's not how you should go about this yourself either. If you try, you'll have to deal with complexities like keeping your own globals separate from the user's, handling statements as well as expressions, handling multi-line statements and expressions (doing raw_input(sys.ps2) is easy, but how do you know when to do that?), interacting properly with readline and rlcomplete, etc.
There's a section of the documentation called Custom Python Interpreters which explains the easy way to do this:
The modules described in this chapter allow writing interfaces similar to Python’s interactive interpreter. If you want a Python interpreter that supports some special feature in addition to the Python language, you should look at the code module.
And code:
… provides facilities to implement read-eval-print loops in Python. Two classes and convenience functions are included which can be used to build applications which provide an interactive interpreter prompt.
The idea is that you let Python do all the hard stuff, up to whatever level you want to take over, and then you just write the part on top of that.
You may want to look at the source for IDLE, ipython, bpython, etc. for ideas.
Instead of using exec() to run the user input, try eval():
retval = eval(user_input)
sys.stdout.write(repr(retval) + "\n")