Using re.sub with jinja2.Markup escaping in Python 3.6? - python-3.x

So I have the following function in my Flask app.
def markup_abbreviations(txt, match_map, match_regex):
html = Markup.escape(txt)
sub_template = Markup('<abbr title="%s">%s</abbr>')
def replace_callback(m):
return sub_template % (match_map[m.group(0)], m.group(0))
return match_regex.sub(replace_callback, html)
Example arguments:
txt = 'blah blah blah etc., blah blah'
match_map = {
'etc.': 'et cetera',
'usu.': 'usually',
}
match_regex = re.compile(
'|'.join(r'\b' + re.escape(k) for k in match_map)
)
This was working very well and turning "etc." into "<abbr title=\"et cetera\">etc.</abbr>" and so on in my local Python 3.3 machine.
Then I figure I want to deploy to Heroku, and it says it only supports the latest python, which is Python 3.6.1. It's different from the one I got locally, but, eh, whatever. It works... mostly.
Except my function above gives me "<abbr title="et cetera">etc.</abbr>" now.
I assume between Python 3.3 and Python 3.6 the re.sub implementation must have changed somehow and now it no longer uses the passed string methods to create the output. So Markup's auto-escaping methods aren't used. Instead a new str is built from scratch. Which is why re.sub only returns str now, and not Markup anymore.
How can I use re.sub with jinja2.Markup in Python 3.6 and make my function work once again?

The Markup class just "mark" string as safe for html. It means that the string doesn't have to be escaped when it placed in to the template.
When the re.sub() return new str object what you have to do is mark the new object as safe (wrap it in the Markup).
def markup_abbreviations(txt, match_map, match_regex):
html = Markup.escape(txt)
sub_template = '<abbr title="%s">%s</abbr>'
def replace_callback(m):
return sub_template % (match_map[m.group(0)], m.group(0))
return Markup(match_regex.sub(replace_callback, html))
I check all "What's new" from Python 3.3 to 3.6 and there is nothing about changing behavior of re module (well there is something but it shouldn't be connected with your problem). Maybe someone else know what happend...

Related

exec() not working when trying to execute a string containing the command "abs.__doc__"

I am trying to execute the command abs.__ doc__ inside the exec() function but for some reason it does not work.
function = input("Please enter the name of a function: ")
proper_string = str(function) + "." + "__doc__"
exec(proper_string)
Essentially, I am going through a series of exercises and one of them asks to provide a short description of the entered function using the __ doc__ attribute. I am trying with abs.__ doc__ but my command line comes empty. When I run python in the command line and type in abs.__ doc__ without anything else it works, but for some reason when I try to input it as a string into the exec() command I can't get any output. Any help would be greatly appreciated. (I have deliberately added spaces in this description concerning the attribute I am trying to use because I get bold type without any of the underscores showing.)
As a note, I do not think I have imported any libraries that could interfere, but these are the libraries that I have imported so far:
import sys
import datetime
from math import pi
My Python version is Python 3.10.4. My operating system is Windows 10.
abs.__doc__ is a string. You should use eval instead of exec to get the string.
Example:
function = input("Please enter the name of a function: ")
proper_string = str(function) + "." + "__doc__"
doc = eval(proper_string)
You can access it using globals():
def func():
"""Func"""
pass
mine = input("Please enter the name of a function: ")
print(globals()[mine].__doc__)
globals() return a dictionary that keeps track of all the module-level definitions. globals()[mine] is just trying to lookup for the name stored in mine; which is a function object if you assign mine to "func".
As for abs and int -- since these are builtins -- you can look it up directly using getattr(abs, "__doc__") or a more explicit: getattr(__builtins__, "abs").__doc__.
There are different ways to lookup for a python object corresponding to a given string; it's better not to use exec and eval unless really needed.

Global variable values in PyCharm (Python 3.6) console

I'm new to both Python and PyCharm, so please forgive ignorance.
I was trying to tech myself about the execution of functions when initialising classes - specifically, I want to re-use a database connection object if passed into a new instance, but create one if not. I have a function get_cnx() that creates a connection. I discovered that, whether using a default argument in the __init__ statement to call get_cnx():
def __init__(self, db_cnx=get_cnx())
...or whether using a keyword argument:
self.db_cnx = kwargs.get('db_cnx', get_cnx())
...the function is always executed regardless of the presence (or content) of the connection argument that's passed in. Defeats the object of re-using a connection, so I reverted to an if condition. I believe there's a way of doing this with a decorator, but that felt like gilding the Lilly.
Anyway, this is the context for my actual question: to help me work out what was going on I created this simple test, as a module called "classes.py":
greeting = 'Good Day'
def my_func():
global greeting
greeting = 'Changed'
return 'Hello'
class Animal:
def __init__(self, greet):
if not greet:
self.greet = my_func()
else:
self.greet = greet
if __name__ == '__main__':
cat = Animal(None)
If I run this module (with "Run with Python console" checked in the configuration), I see the global variable greeting shown in blue as 'Changed', which is what I'd expect.
If I change the last bit to this:
if __name__ == '__main__':
cat = Animal('Wotcha')
I see the global variable shown in blue as 'Good Day', which is also what I'd expect.
However, when I then type this into the console:
dog = Animal(None)
...the global variable name turns red but still shows 'Good Day'.
Similarly, using the PyCharm console does the same thing:
>>> print(greeting)
Good Day
>>> dog = Animal(None)
>>> print(greeting)
Good Day
Now, I loaded the module into IDLE and hit F5 (run module), and in the console, did this:
>>> greeting
'Good Day'
>>> dog = Animal(None)
>>> greeting
'Changed'
This is what I would have expected to see in the PyCharm console.
Can someone explain what's going on? Could it be a bug, or is it my lack of understanding of the way PyCharm deals with scope? Or my lack of broader understanding of execution scope?
Thanks!!
JetBrains have opened a bug report for me - confirmed the behaviour isn't as expected.

The unbearable opaqueness of time.struct_time

Why do pylint and the intellisense features of IDEs have trouble recognizing instances of time.struct_time? The following code contains some trivial tests of existent/non-existent attributes of classes, named tuples and the named-tuple-like time.struct_time. Everything works as expected in pylint, IntelliJ and VSCode - the access to missing attributes is reported in each case except for time.struct_time - it generates no warnings or errors in any of these tools. Why can't they tell what it is and what its attributes are?
import time
from collections import namedtuple
t = time.localtime()
e = t.tm_mday
e = t.bad # this is not reported by linters or IDEs.
class Clz:
cvar = 'whee'
def __init__(self):
self.ivar = 'whaa'
o = Clz()
e = Clz.cvar
e = o.ivar
e = Clz.bad
e = o.bad
Ntup = namedtuple('Ntup', 'thing')
n = Ntup(thing=3)
e = n.thing
e = n.bad
The context of the question is the following recent bug in pipenv -
# Halloween easter-egg.
if ((now.tm_mon == 10) and (now.tm_day == 30))
Obviously, the pass path was never tested but it seems the typical static analysis tools would not have helped here either. This is odd for a type from the standard library.
(Fix can be seen in full at https://github.com/kennethreitz/pipenv/commit/033b969d094ba2d80f8ae217c8c604bc40160b03)
time.struct_time is an object defined in C, which means it can't be introspected statically. The autocompletion software can parse Python code and make a reasonable guess as to what classes and namedtuples support, but they can't do this for C-defined objects.
The work-around most systems use is to generate stub files; usually by introspecting the object at runtime (importing the module and recording the attributes found). For example, CodeIntel (part of the Komodo IDE), uses an XML file format called CIX. However, this is a little more error-prone so such systems then err on the side of caution, and will not explicitly mark unknown attributes as wrong.
If you are coding in Python 3, you could look into using type hinting. For C extensions you still need stub files, but the community is pretty good at maintaining these now. The standard library stub files are maintained in a project called typeshed.
You'd have to add type hints to your project:
#!/usr/bin/env python3
import time
from collections import namedtuple
t: time.struct_time = time.localtime()
e: int = t.tm_mday
e = t.bad # this is not reported by linters or IDEs.
class Clz:
cvar: str = 'whee'
ivar: str
def __init__(self) -> None:
self.ivar = 'whaa'
o = Clz()
s = Clz.cvar
s = o.ivar
s = Clz.bad
s = o.bad
Ntup = namedtuple('Ntup', 'thing')
n = Ntup(thing=3)
e = n.thing
e = n.bad
but then the flake8 tool combined with the flake8-mypy plugin will detect the bad attributes:
$ flake8 test.py
test.py:8:5: T484 "struct_time" has no attribute "bad"
test.py:22:5: T484 "Clz" has no attribute "bad"
test.py:23:5: T484 "Clz" has no attribute "bad"
test.py:28:5: T484 "Ntup" has no attribute "bad"
PyCharm builds on this work too, and perhaps can detect the same invalid use. It certainly directly supports pyi files.

Is there a way to declare a Groovy string format in a variable?

I currently have a fixed format for an asset management code, which uses the Groovy string format using the dollar sign:
def code = "ITN${departmentNumber}${randomString}"
Which will generate a code that looks like:
ITN120AHKXNMUHKL
However, I have a new requirement that the code format must be customizable. I'd like to expose this functionality by allowing the user to set a custom format string such as:
OCP${departmentNumber}XI${randomString}
PAN-${randomString}
Which will output:
OCP125XIBQHNKLAPICH
PAN-XJKLBPPJKLXHNJ
Which Groovy will then interpret and replace with the appropriate variable value. Is this possible, or do I have to manually parse the placeholders and manually do the string.replace?
I believe that GString lazy evaluation fits the bill:
deptNum = "C001"
randomStr = "wot"
def code = "ITN${deptNum}${->randomStr}"
assert code == "ITNC001wot"
randomStr = "qwop"
assert code == "ITNC001qwop"
I think the original poster wants to use a variable as the format string. The answer to this is that string interpolation only works if the format is a string literal. It seems it has to be translated to more low level String.format code at compile time. I ended up using sprintf
baseUrl is a String containing http://example.com/foo/%s/%s loaded from property file
def operation = "tickle"
def target = "dog"
def url = sprintf(baseUrl, operation, target)
url
===> http://example.com/foo/tickle/dog
I believe in this case you do not need to use lazy evaluation of GString, the normal String.format() of java would do the trick:
def format = 'ITN%sX%s'
def code = { def departmentNumber, def randomString -> String.format(format, departmentNumber, randomString) }
assert code('120AHK', 'NMUHKL') == 'ITN120AHKXNMUHKL'
format = 'OCP%sXI%s'
assert code('120AHK', 'NMUHKL') == 'OCP120AHKXINMUHKL'
Hope this helps.
for Triple double quoted string
def password = "30+"
def authRequestBody = """
<dto:authTokenRequestDto xmlns:dto="dto.client.auth.soft.com">
<login>support#soft.com</login>
<password>${password}</password>
</dto:authTokenRequestDto>
"""

QComboBox.currentText() -- PySide vs PyQt4

I've got a python script using PySide and it works fine.
But then I thought to check if it gonna work with PyQt4.
And after changing the import strings to PyQt4, things went wrong.
The error points to the subject, as follows:
File "./my_file.py", line 93, in showit
curr_query = '.'.join(curr_query)
TypeError: sequence item 0: expected string, QString found
From the docs I can see that PySide subject method returns 'unicode' string,
but the PyQt4 one returns QString object.
Playing with the str(), str() etc did not seem to do the job.
Here's the function code:
def showit(self, idx):
curr_query = []
for i in xrange(idx+1):
>> x = self.combo[i].currentText()
>> if x:
curr_query.append(x)
else:
break
curr_query = '.'.join(curr_query)
This reads text of a set of QCombobox'es to build up a dot-sepated string presentation that I use later.
The marked '>>' lines is where the issue occurs - the 'x' object is never an empty string, suddenly, as it was while using PySide library. But it is expected to be empty, if there's an empty self.combo[i] .
I've searched the SO archive and found this answer but not able to use it.
Please advice how to fix this.
You need to convert your x values to a string of sorts. Something like
curr_query.append(str(x))
should do the trick.

Resources