I want to make a function that can determine the source code of how it was called. I'm aware of how to do this generally with the inspect module. For example, this question, works well and provides my desired output in the lines variable as shown below:
def hello(x):
frame,filename,line_number,function_name,lines,index=\
inspect.getouterframes(inspect.currentframe())[1]
print(frame,filename,line_number,function_name,lines,index)
The problem is that this solution doesn't work in an interactive command line session. For example, from a command line, the result looks like:
>>> y = hello(7)
(<frame object at 0x01ECA9E8>, '<stdin>', 1, '<module>', None, None)
The problem is that the source file is '<stdin>', so the lines variable is None. How can I access the calling line to find the result containing the string y = hello(7) during an interactive session?
It may not be possible, as #abarnert says. There are at least partial workarounds, however.
Getting source code lines isn't the biggest problem; modules like readline keep track of them. Interactive Python and iPython expose their lines slightly differently (sigh), but that too can be equalized. (My show package, for example, does this; its linecacher module puts a veneer on to equalize source access for normal Python and the different interactive flavors.)
The bigger problem is that, even once you have the source code, inspect doesn't provide legitimate line numbers (e.g. inspect.currentframe().f_back.f_lineno works great in normal code, but gives values like 1 or the point of the call in <stdin> when called interactively.)
But I'm not quite ready to call it impossible. Based on tracebacks generated by interactive Python and iPython, it appears that there may be sufficient information available to reconstruct "where did this call come from?" How much effort that would take, and how robust the answers would be...those are open questions.
Related
I'm facing some problems trying to load a full python script from my pastebin/github pages.
I followed this link, trying to convert the raw into a temp file and use it like a module: How to load a python script from a raw link (such as Pastebin)?
And this is my test (Using a really simple python script as raw, my main program is not so simple unfortunately): https://trinket.io/python/0e95ba50c8
When I run the script (that now is creating a temp file in the current directory of the .py file) I get this error:
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\BOT\\Images\\tempxm4xpwpz.py'
Otherwise I also treid the exec() function... No better results unfortunately.
With this code:
import requests as rq
import urllib.request
def main():
code = "https://pastebin.com/raw/MJmYEKqh"
response = urllib.request.urlopen(code)
data = response.read()
exec(data)
I get this error:
File "<string>", line 10, in <module>
File "<string>", line 5, in hola
NameError: name 'printest' is not defined
Since my program is more complex compared to this simple test, I don't know how to proceed...
Basically What I want to achieve is to write the full script of my program on GitHub and connect it to a .exe so if I upgrade the raw also my program is updated. Avoiding to generate and share (only with my friends) a new .exe everytime...
Do you think is possible? If so.. what am I doing wrong?
PS: I'm also open to other possibilities to let my friends update the program without downloading everytime the .exe, as soon as they don't have to install anything (that's why I'm using .exe).
Disclaimer: it is really not a good idea to run an unverified (let alone untrusted) code. That being said if you really want to do it...
Probably the easiest and "least-dirty" way would be to run whole new process. This can be done directly in python. Something like this should work (inspiration from the answer you linked in your question):
import urllib.request
import tempfile
import subprocess
code = "https://pastebin.com/raw/MJmYEKqh"
response = urllib.request.urlopen(code)
data = response.read()
with tempfile.NamedTemporaryFile(suffix='.py') as source_code_file:
source_code_file.write(data)
source_code_file.flush()
subprocess.run(['python3', source_code_file.name])
You can also make your code with exec run correctly:
What may work:
exec(data, {}) -- All you need to do, is to supply {} as second argument (that is use exec(data, {})). Function exec may receive two additional optional arguments -- globals and locals. If you supply just one, it will use the same directory for locals. That is the code within the exec would behave like sort-of "clean" environment, at the top-level. Which is something you aim for.
exec(data, globals()) -- Second option is to supply the globals from your current scope. This will also work, though you probably has no need to give the execucted code access to your globals, given that that code will set-up everything inside anyway
What does not work:
exec(data, {}, {}) -- In this case the executed code will have two different dictionaries (albeit both empty) for locals and globals. As such it will behavie "as-in" (I'm not really sure about this part, but as I tested it, it seams as such) the function. Meaning that it will add the printest and hola functions to the local scope instead of global scope. Regardless, I expected it to work -- I expected it will just query the printest in the hola function from the local scope instead of global. However, for some reason the hola function in this case gets compiled in such a way it expects printest to be in global scope and not local, which is not there. I really did not figured out why. So this will result in the NameError
exec(data, globals(), locals()) -- This will provide access to the state from the caller function. Nevertheless, it will crash for the very same reason as in the previous case
exec(data) -- This is just a shorthand for exec(data, globals(), locals()
I'd like to get help(obj) text in IPython or Jupyter notebook in a non-interactive manner, into some variable instead of it being displayed.
Note: help provides more information than obj.__doc__ provides, so that's not quite an alternative.
Short answer:
import pydoc
help_result_string = pydoc.render_doc(obj)
Longer answer:
When you call help(obj), it is a wrapper to pydoc.help(obj) (see help.__doc__), which is the same as pydoc.Helper()(obj) (from pydoc source: help = Helper()), which usually leads to pydoc.doc(obj), which writes the resulting string from pydoc.render_doc(obj) to the standard output or a pager, depending on the system.
Details on what other things can happen when help(obj) is called can be found in the pydoc source code.
I like to use ipdb to debug my code. I know we could stop the code on a file on a specific line with b(reak) file:lineno. That command will set a breakpoint in file at line 'no'.
Actually, I have inserted import ipdb; ipdb.set_trace() on a specific file. Each time I use the command s(tep), it executes and step into functions. My problem is it is too slow before seeing what I want to see. The stacktrace showed me lines I do not necessarily want to see. Then I was thinking to put a breakpoint on all files from a certain directory, i.e., b mydirectory/**. Therefore, eachtime I will execute c, it will show me all lines I want to see. However, I can't execute such command (i.e., b mydirectory/**). Could anyone have a solution to this problem?
Thanks!
P.S. The following picture show ton of those irrelevant files I don't want to see. In fact, it is normal to see those files, because I am working on a django project.
Please tell me if the question is unclear
import pdb; pdb.Pdb(skip=['mydirectory.*']).set_trace()
mydirectory have to be a python module, here is more info from the documentaion
The skip argument, if given, must be an iterable of glob-style module
name patterns. The debugger will not step into frames that originate
in a module that matches one of these patterns. [1]
source: https://docs.python.org/2/library/pdb.html#pdb.Pdb
These days, I'm transitiong from Matlab to Python after using Matlab/Octave for more than ten years. I have two quick questions:
In the Python interactive mode, is there anything corresponding to Matlab's ans?
How can I run shell commands in the Python interactive mode? Of course, I can use os.system(), but in Matlab we may run shell commands just by placing ! before the actual command. Is there anything similar in Python?
Python interactive mode is Python. You will need to use os.system or an equivalent. Alternately, you can suspend Python with Ctrl-Z, and get back into it with fg. (Assuming UNIX-like environment.)
The last evaluated expression is saved in the variable _:
>>> 1 + 2
3
>>> _ * 4
12
The Python equivalent of the Matlab ans is as follows:
ans = (your_expression)
In other words, the most recent expression is not always automatically saved to a default reference, so just save it manually as normal. As #Amadan points out, expressions are sometimes saved to _, but not always. The best practice for reliability and clarity is to save it yourself.
To run shell commands, you can use os.system() as you suggested. However, that is deprecated, so you should look into the subprocess module.
You probably want to use the IPython shell (now part of the jupyeter project). In the IPython shell you can also run system commands using !, although many basic commands (like ls or cd) work without even needing to !. Unlike in MATLAB, you don't need to pass it as a string (although you can). So !ls works fine in IPython, while in MATLAB you would need to do !'ls'. Further, you can assign the results to a variable in IPython, which you can't do in MATLAB. So a = !ls works in IPython but not in MATLAB. Further, if you use !!, the result is returned in a form easily usable in Python. So !!ls returns a list of file names.
IPython still uses the _ notation for getting the previous result (except, as with Python, None is counted as "no result" and thus is not recorded). You can also get the second-to-last result with __ and the third-to-last with ___. Further, IPython puts a number next to each line in the command prompt. To get the result of a particular line, just do _n where n is the number. So to get the result of the 3rd command, which has the number 3 next to it, just do _3. This still doesn't work if the result is None, though.
It has a ton of features. You can get the previous input (as a string) with _i (and so on, following the same pattern as with the outputs). You can time code with %timeit and %%timeit. You can jump into the debugger after encountering an error.
I know this is wrong thing to do, but I am using python 3 but studying it with python 2 book.
it says,
>>>range(2,7)
will show
[2,3,4,5,6]
but I know it won't show the output above, THAT I figured. so I tried:
>>>>print(range(2,7))
and ta-da- it shows follow:
range(2,7)
looks like this is the one of changes from P2 to P3 so I tried:
list(range(2,7))
this one works ok on IDLE but not ok on notepad for long coding. so finally I tried:
print(list(range(2,7)))
and it showed something similar to what I intended... Am I doing right? Is this the only way to write it?
In your IDLE case, you are running the code in IDLE's PyShell window. This is running the interactive interpreter. In interactive mode, Python interprets immediately each line you type in and it displays the value returned by evaluating the statement you typed plus anything written to standard output or standard error. For Python 2, range() returns a list and, as you discovered, in Python 3, it returns an iterable range() object which you can use to create a list object or use elsewhere in iteration contexts. The Python 3 range() is similar to Python 2's xrange().
When you edit a file in an editor like Notepad, you are writing a script file and when you run the file in the Python interpreter, the whole script is interpreted and run as a unit, even if it is only one line long. On the screen, you only see what is written to standard output (i.e. "print()") or standard error (i.e. error tracebacks); you don't see the results of the evaluation of each statement as you do in interactive mode. So, in your example, when running from a script file, if you don't print the results of evaluating something you won't see it.
The Python tutorial talks a bit about this here.
If your only goal is to get back the list representation, what you're doing is correct. Python 3.0 now treats range as returning an iterator (what xrange used to do)