Bare words / new keywords in Python - python

I wanted to see if it was possible to define new keywords or, as they're called in WAT's Destroy All Software talk when discussing Ruby, bare words, in Python.
I came up with an answer that I couldn't find elsewhere, so I decided to share it Q&A style on StackOverflow.

I've only tried this in the REPL, outside any block, so far. It may be possible to make it work elsewhere, too.
I put this in my python startup file:
import sys, traceback
def bareWordsHandler(type_, value, traceback_):
if isinstance(value, SyntaxError):
import traceback
# You can probably modify this next line so that it'll work within blocks, as well as outside them:
bareWords = traceback.format_exception(type_, value, traceback_)[1].split()
# At this point we have the raw string that was entered.
# Use whatever logic you want on it to decide what to do.
if bareWords[0] == 'Awesome':
print(' '.join(bareWords[1:]).upper() + '!')
return
bareWordsHandler.originalExceptHookFunction(type_, value, traceback_)
bareWordsHandler.originalExceptHookFunction = sys.excepthook
sys.excepthook = bareWordsHandler
Quick REPL session demonstration afterwords:
>>> Awesome bare words
BARE WORDS!
Use responsibly.
Edit: Here's a more useful example. I added in a run keyword.
if bareWords[0] == 'from' and bareWords[2] == 'run':
atPrompt.autoRun = ['from ' + bareWords[1] + ' import ' + bareWords[3].split('(')[0],
' '.join(bareWords[3:])]
return
atPrompt.autoRun is a list of variables that, when my prompt is displayed, will automatically be checked and fed back. So, for example, I can do this:
>>> from loadBalanceTester run loadBalancerTest(runJar = False)
And this gets interpreted as:
from loadBalancerTest import loadBalancerTest
loadBalancerTest(runJar = False)
It's kind of like a macro - it's common for me to want to do this kind of thing, so I decided to add in a keyword that lets me do it in fewer keystrokes.

Related

Apply textwrap.fill to every print on python script

I would like to have my print output length limited to X characters.
I've been looking for some info and I found the command textwrap.fill which makes just what I was looking for by using something like:
print(textwrap.fill("Hello world", X))
However, I was wondering if there is a way to apply this length limitation to every print without having to write it (I do have plenty of them) by creating or setting a class or something at the very beginning of the script.
Monkeypatching print is doable, but it's not a good idea. For one thing, what do you want to happen if someone does a print(spam, eggs)? Or print(spam, end='')? Or print(spam, file=outfile)?
A better solution is probably replacing sys.stdout with a wrapper.
The normal sys.stdout is a plain old text file object, a TextIOWrapper just like the ones you get from open, except that when you write to it, it goes to the console instead of to a file on disk.
And you're allowed to replace it with anything else that meets the TextIOBase protocol.
And writing a TextIOBase is really simple. All you really need to implement is write and/or read and readline (depending on whether you're wrapping output, input, or both), and all our wrapper needs to do in write is to buffer up lines, fill them, and pass them to the real file object underneath.
Like this:
import io
import sys
import textwrap
class Filler(io.TextIOBase):
def __init__(self, file, width=70):
self.file = file
self.textwrapper = textwrap.TextWrapper(width=width)
self.buf = ''
def write(self, buf):
self.buf += buf
lines = self.buf.split('\n')
self.buf = lines.pop()
for line in lines:
self.file.write(self.textwrapper.fill(line) + '\n')
def close(self):
if self.buf:
self.file.write(self.textwrapper.fill(buf))
self.buf = ''
self.file.close()
sys.stdout = Filler(sys.stdout, 32)
print('Spam spam spam spammity ' * 10)
print('Spam', 'Eggs')
sys.stdout.textwrapper.width = 72
print('Spam ' + 'spam ' * 50, 'and eggs', sep='... ')
print('Spam', end=' ')
print('Eggs', end=' ')
print('Cheese')
Technically, I think I may be cheating in a few ways here:
The docs say the ABC TextIOBase wants detach, read, and readline, even if they don't make sense here. But the ABC doesn't seem to enforce them as abstract methods, so I didn't bother.
I think it's legal (and it works) to leave encoding and errors set to None, since we're just passing through to another TextIOBase and expecting it to do the encoding, but I can't find anything that says it's legal. And if some code were to test sys.stdout.encoding to see if it's UTF-8 or something, that might be trouble.
Similarly for newlines. And, since I haven't tested on Windows, I can't be as sure that it works.
Also, forwarding other methods to self.file might be a good idea, like fileno() and isatty(). But I'd worry that any app that wants to access stdout as a TTY probably need to know about the Filler that we stuck in front of it, not just transparently go through it.
This is of course all Python 3-specific. In Python 2:
sys.stdout is a file, not a TextIOWrapper. The API you need to wrap is a bit different, and not nearly as well defined.
Unless you __future__ up the 3.x-style print function, print is a statement, so you can't monkeypatch it. (I mean, you could write an import hook that bytecode-hacks out every PRINT_* bytecode, or maybe even inject a .so that replaces PyObject_Print… but who cares anyway? It's Python 2.)

Apply function decorator on print function across all files without having to import and/or reapply?

Edit: My first attempt at asking this might be a bit unfocused/poorly worded here's a better explanation of what I'm trying to do:
I'm trying to modify the default behavior of the print function for the entire environment python is running in without having to modify each file that's being run.
I'm attempting to decorate the print function (I know there are many ways to do this such as overriding it but that's not really the question I'm asking) so I can have it print out some debugging information and force it to always flush. I did that like so:
def modify_print(func):
# I made this so that output always gets flushed as it won't by default
# within the environment I'm using, I also wanted it to print out some
# debugging information, doesn't really matter much in the context of this
# question
def modified_print(*args,**kwargs):
return func(f"some debug prefix: ",flush=True,*args,**kwargs)
return modified_print
print = modify_print(print)
print("Hello world") # Prints "some debug prefix Hello World"
However what I'm trying to do is modify this behavior throughout my entire application. I know I can manually decorate/override/import the print function in each file however I'm wondering if there is some way I can globally configure my python environment to decorate this function everywhere. The only way I can think to do this would be to edit the python source code and build the modified version.
EDIT:
Here's the behavior I wanted implemented, thank you Match for your help.
It prints out the line number and filename everywhere you call a print function within your python environment. This means you don't have to import or override anything manually in all of your files.
https://gist.github.com/MichaelScript/444cbe5b74dce2c01a151d60b714ac3a
import site
import os
import pathlib
# Big thanks to Match on StackOverflow for helping me with this
# see https://stackoverflow.com/a/48713998/5614280
# This is some cool hackery to overwrite the default functionality of
# the builtin print function within your entire python environment
# to display the file name and the line number as well as always flush
# the output. It works by creating a custom user script and placing it
# within the user's sitepackages file and then overwriting the builtin.
# You can disable this behavior by running python with the '-s' flag.
# We could probably swap this out by reading the text from a python file
# which would make it easier to maintain larger modifications to builtins
# or a set of files to make this more portable or to modify the behavior
# of more builtins for debugging purposes.
customize_script = """
from inspect import getframeinfo,stack
def debug_printer(func):
# I made this so that output always gets flushed as it won't by default
# within the environment I'm running it in. Also it will print the
# file name and line number of where the print occurs
def debug_print(*args,**kwargs):
frame = getframeinfo(stack()[1][0])
return func(f"{frame.filename} : {frame.lineno} ", flush=True,*args,**kwargs)
return debug_print
__builtins__['print'] = debug_printer(print)
"""
# Creating the user site dir if it doesn't already exist and writing our
# custom behavior modifications
pathlib.Path(site.USER_SITE).mkdir(parents=True, exist_ok=True)
custom_file = os.path.join(site.USER_SITE,"usercustomize.py")
with open(custom_file,'w+') as f:
f.write(customize_script)
You can use usercustomize script from the site module to achieve something like this.
First, find out where your user site-packages directory is:
python3 -c "import site; print(site.USER_SITE)"
/home/foo/.local/lib/python3.6/site-packages
Next, in that directory, create a script called usercustomize.py - this script will now be run first whenever python is run.
One* way to replace print is to override the __builtins__ dict and replace it with a new method - something like:
from functools import partial
old_print = __builtins__['print']
__builtins__['print'] = partial(old_print, "Debug prefix: ", flush=True)
Drop this into the usercustomize.py script and you should see all python scripts from then on being overridden. You can temporarily disable calling this script by calling python with the -s flag.
*(Not sure if this is the correct way of doing this - there may be a better way - but the main point is that you can use usercustomize to deliver whatever method you choose).
There's no real reason to define a decorator here, because you are only intending to apply it to a single, predetermined function. Just define your modified print function directly, wrapping it around __builtins__.print to avoid recursion.
def print(*args, **kwargs):
__builtins.__print(f"some debug prefix: ", flush=True, *args, **kwargs)
print("Hello world") # Prints "some debug prefix Hello World"
You can use functools.partial to simplify this.
import functools
print = functools.partial(__builtins.__print, f"some debug prefix: ", flush=True)

Changing variables across files Python

Heyo, I've run upon a problem.
So I have three files:
Main Program
Functions
Data
And I want the Main Program to call a function from the Functions module, which changes a variable in Data. Then I need to use the new variable elsewhere in the Main Program.
This is what I want, shown as a simplified demonstration program:
The Data file:
#data.py
#just an short depiction of my actual file
text = ""
The Functions file:
#functions.py
from data import *
def printHi():
global text
text = "hi"
print(text)
The Main Program:
#mainProgram.py
from functions import *
from data import *
printHi()
print(text)
What I expected would happen would be that when I run the Main Program:
The Functions file and Data file is imported.
Then it calls the "printHi" method from the Functions file.
The variable "text" from the Data file is assigned "hi", and is printed.
The the Main Program prints the "text" variable.
And I supposed that the text variable would be "hi". However, to my disappointment, it prints blank. It does indeed print the initial text value.
I really have NO idea why this is so. Shouldn't the text variable have already been changed? Could you please explain what part about my program is wrong and how to correct it?
The short answer is simply not to do this. It's a Bad Idea for all the reasons that global variables are always bad ideas: because they lead to stack overflow questions that read like "If I do this thing I shouldn't do, it does something I didn't expect -- why did it do that?" Whose easiest answer is, as you've now read, "That's why you shouldn't do that thing."
The long answer is a bit beyond me without spending a whole lot more time on the matter, but the longer answer is simple enough. When you do those "star" imports (from modulename import *) you're rebinding the name of the variable. What functions.printHi thinks of as text is not data.text but actually functions.text. When it's changed in printHi, it changes functions.text which should still be okay, since mainProgram is also importing functions.
However remember that mainProgram ISN'T actually importing functions, it's from functions import *'ing. That means what mainProgram thinks of as text is neither data.text nor functions.text, but mainProgram.text. When functions.printHi changes functions.text, it doesn't touch mainProgram.text.
The short answer applies here because these sorts of pitfalls are non-obvious unless you can think deeply enough about your code to understand them. If you are able to think that deeply about your code, you should be able to write something that can sidestep such pitfalls entirely. For instance: "global mutable state" is generally a bad thing. Avoid it.
To just make this work, drop all your "star" imports. The following code works:
# functions.py
import data
def printHi():
# plus! You don't need the `global` anymore.
data.text = "hi"
print(data.text)
# mainProgram.py
import functions
import data
functions.printHi() # prints "hi" from inside functions.printHi
print(data.text) # also prints "hi"
Cool, we have a lot of people saying "don't do this!". Well, what should you do then? The good way to do this is to pass the text variable into the function and out of it. Like so:
The Data file:
#data.py
#just an short depiction of my actual file
text = ""
The Functions file:
#functions.py
from data import *
def printHi(atext):
atext = "hi"
print(atext)
return atext
The Main Program:
#mainProgram.py
from functions import *
from data import *
text = printHi(text)
print(text)
That solves your problem. You should probably also get rid of the * imports as the other answer suggests but that's a philosophical question.

python jedi: how to retrieve methods of instances?

I built simple text editor with some accessibility feature for screen reading software.
I'm using Python for .NET (pythonnet) to show a form containing a rich text box.
When user press tab after a period it pop-ups a context menu with completions for selected element.
Ok, it works fine with Python objects, but it doesn't work with .net live objects, there is no solution to this problem.
Now, I want build a TreeView object with all names and definitions of module I'm editing.
So, for example I type:
import sys
import os
lst = list()
etc...
If I use jedi.names of my source, I can retrieve os, sys and lst.
For each name, I want retrieve sub definitions, such as functions for sys and os module, and methods for lst.
I can't find a way to do this with jedi:
names = jedi.names(MySource)
names[0].defined_names() # works for sys
names[1].defined_names() # works for os
names[2].defined_names() # doesn't work for lst instance of list().
Any suggestions?
I tried to use more and more editors, but accessibility support is very very bad...
This looks like a bug, where jedi.evaluate.representation.Instance.__getattr__() mistakenly blocks evaluation of .names_dict. I added a pull request to the jedi repository to fix this. In the mean time, you can either add 'names_dict' to the whitelist in Instance.__getattr__() in your copy of jedi/evaluate/representation.py, or use the code below to patch this method automatically for the current session.
import jedi
def patch_jedi():
__old__getattr__ = jedi.evaluate.representation.Instance.__getattr__
def __patched__getattr__(self, name):
if name == 'names_dict':
# do a simplified version of __old__getattr__, bypassing the name check
return getattr(self.base, name)
else:
# use standard behavior
return __old__getattr__(self, name)
# test whether jedi has been updated to avoid the Instance.defined_names() bug
try:
jedi.names("lst = list()")[0].defined_names()
except AttributeError as e:
if e.args[0].startswith("Instance ") and e.args[0].endswith("Don't touch this (names_dict)!"):
# patch jedi to avoid this error
print "patching jedi"
jedi.evaluate.representation.Instance.__getattr__ = __patched__getattr__
else:
# something else strange is going on
raise
patch_jedi()
print jedi.names("lst = list()")[0].defined_names()
# or: print jedi.Script("lst = list()").goto_definitions()[0].defined_names()
I should note that I'm not familiar with jedi, so I don't know whether defined_names() is supposed to work for definitions that create instances. The code above won't fix references like jedi.names("lst = []")[0].defined_names(), and there's no obvious patch to do that. So there may be something deeper going on that I don't know about. Hopefully the developer will help set this straight in response to that pull request.

Python: how to input python-code part like \input in Tex?

I want to input code in Python like \input{Sources/file.tex}. How can I do it in Python?
[added]
Suppose I want to input data to: print("My data is here"+<input data here>).
Data
1, 3, 5, 5, 6
The built-in execfile function does what you ask, for example:
filename = "Sources/file.py"
execfile( filename )
This will execute the code from Sources/file.py almost as if that code were embedded in the current file, and is thus very similar to #include in C or \input in LaTeX.
Note that execfile also permits two optional arguments allowing you to specify the globals and locals dicts that the code should be executed with respect to, but in most cases this is not necessary. See pydoc execfile for details.
There are occasional legitimate reasons to want to use execfile. However, for the purpose of structuring large Python programs, it is conventional to separate your code into modules placed somewhere in the PYTHONPATH and to load them using the import statement rather than executing them with execfile. The advantages of import over execfile include:
Imported functions get qualified with the name of the module, e.g. module.myfunction instead of just myfunction.
Your code doesn't need to hard-code where in the filesystem the file is located.
You can't do that in Python. You can import objects from other modules.
otherfile.py:
def print_hello():
print "Hello World!"
main.py
import otherfile
otherfile.print_hello() # prints Hello World!
See the python tutorial
Say you have code in "my_file.py". Any line which is not in a method WILL get executed when you do:
import my_file
So for example if my_file.py has the following code in it:
print "hello"
Then in the interpreter you type:
import my_file
You will see "hello".
My question was clearly too broad, as the variety of replies hint -- none of them fully attack the question. The jchl targets the scenario where you get python-code to be executed. The THC4k addresses the situation where you want to use outside objects from modules. muckabout's reply is bad practice, as Xavier Ho mentioned, why on earth it uses import when it could use exec as well, the principle of least privileges to the dogs. One thing is still missing, probably because of the conflict between the term python-code in the title and the addition of data containing integers -- it is hard to claim that data is python-code but the code explains how to input data, evaluations and executable code.
#!/usr/bin/python
#
# Description: it works like the input -thing in Tex,
# you can fetch outside executable code, data or anything you like.
# Sorry I don't know precisely how input(things) works, maybe misusing terms
# or exaggerating.
#
# The reason why I wanted input -style thing is because I wanted to use more
# Python to write my lab-reports. Now, I don't need to mess data with
# executions and evalutions and data can be in clean files.
#TRIAL 1: Execution and Evaluation not from a file
executeMe="print('hello'); a = 'If you see me, it works'";
exec( executeMe )
print(a);
#TRIAL 2: printing file content
#
# and now with files
#
# $ cat IwillPrint007fromFile
# 007
f = open('./IwillPrint007fromFile', 'r');
msg = f.read()
print("If 007 == " + msg + " it works!");
# TRIAL 3: Evaluation from a file
#
# $cat IwillEvaluateSthing.py
# #!/usr/bin/python
# #
# # Description:
#
#
# evaluateMe = "If you see me again, you are breaking the rules of Sky."
f = open('./IwillEvaluateSthing.py', 'r');
exec(f.read());
print(evaluateMe);

Categories

Resources