Incompatibility between Python 3.2 and Qt? - python-3.x

I have problems with Python 3.2 and PyQt 4.8.6
It seems as if Python 3.2 can`t find the imports.
Especially the "Q"-methods. For example the QString below.
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
With Python 2.7 everything works fine.
Where is the mistake in my code?

Python3 made many incompatible changes in order to "clean up" the language, and, to a certain extent, PyQt has done the same by introducing "more pythonic" versions of some APIs. But these different API versions can be selected on a class by class basis for both Python2 and Python3, so the only real difference is the defaults chosen for each Python version.
In Python2, the default API version for QString is "v1", which implements it as a Python type; in Python3 the default is "v2", which automatically converts to and from the appropriate Python string object.
The API version can be selected by using the setapi function from the sip package. So to continue using the QString class in your application, just make sure the appropropriate version is set before the PyQt modules are first imported:
import sip
sip.setapi('QString', 1)
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
For details of all the APIs that can be set this way, see here.

Take a look at the notes about Python 3 in the PyQt Reference Guide.
The QString class is implemented as a mapped type that is
automatically converted to and from a Python string. In addition a
None is converted to a null QString. However, a null QString is
converted to an empty Python string (and not None). (This is because
Qt often returns a null QString when it should probably return an
empty QString.)
I've not moved any code over to Python 3 yet, but I believe that the idea is to use normal Python strings instead of QStrings. PyQt will accept them and they already support unicode in Python 3. Where normally a PyQt function would return a QString it will return a regular python string under Python 3.
Have a look at the other differences on the linked page too.

Related

Function Declaration using specific datatypes

def StrongestNeighbour(neighbours: list[int])-> list[int]:
this kind of function declaration is only working in my vs code but not in spyder what is the problem.
Probably with Spyder you are running Python version <3.9+ and with VSCode is Python >=3.9+
The thing is that before Python 3.9, for type hints like the one you show, the syntax is a little bit different:
from typing import List
def strongestNeighbour(neighbours: List[int]) -> List[int]:
return neighbours
strongestNeighbour([1,2,3,4])
Note the import from the typing module and the uppercase L.
You can check the Python 3.8 typing docs for more information on the import and the overall syntax: https://docs.python.org/3.8/library/typing.html
However, if you use Python 3.9 then you can use the list[int] syntax as you have it. You can check the Python 3.9 typing docs for more information: https://docs.python.org/3.9/library/typing.html

How to implement a Meta Path Importer on Python 3

I'm struggling to refactor some working import-hook-functionality that served us very well on Python 2 the last years... And honestly I wonder if something is broken in Python 3? But I'm unable to see any reports of that around so confidence in doing something wrong myself is still stronger! Ok. Code:
Here is a cooked down version for Python 3 with PathFinder from importlib.machinery:
import sys
from importlib.machinery import PathFinder
class MyImporter(PathFinder):
def __init__(self, name):
self.name = name
def find_spec(self, fullname, path=None, target=None):
print('MyImporter %s find_spec fullname: %s' % (self.name, fullname))
return super(MyImporter, self).find_spec(fullname, path, target)
sys.meta_path.insert(0, MyImporter('BEFORE'))
sys.meta_path.append(MyImporter('AFTER'))
print('sys.meta_path:', sys.meta_path)
# import an example module
import json
print(json)
So you see: I insert an instance of the class right in front and one at the end of sys.meta_path. Turns out ONLY the first one triggers! I never see any calls to the last one. That was different in Python 2!
Looking at the implementation in six I thought, well THEY need to know how to do this properly! ... 🤨 I don't see this working either! When I try to step in there or just put some prints... Nada!
After all:IF I actually put my Importer first in the sys.meta_path list, trigger on certain import and patch my module (which all works fine) It still gets overridden by the other importers in the list!
* How can I prevent that?
* Do I need to do that? It seems dirty!
I have been heavily studying the meta_path in Python3.8
The entire import mechanism has been moved from C to Python and manifests itself as sys.meta_path which contains 3 importers. The Python import machinery is cleverly stupid. i.e. uncomplex.
While the source code of the entire python import is to be found in importlib/
meta_path[1] pulls the importer from frozen something: bytecode=?
underscore import is still the central hook called when you "import mymod"
--import--() first checks if the module has already been imported in which case it retrieves it from sys.modules
if that doesn't work it calls find_spec() on each "spec finder" in meta_path.
If the "spec finder" is successful it return a "spec" needed by the next stage
If none of them find it, import fails
sys.meta_path is an array of "spec finders"
0: is the builtin spec finder: (sys, _sre)
1: is the frozen import lib: It imports the importer (importlib)
2: is the path finder and it finds both library modules: (os, re, inspect)
and your application modules based on sys.path
So regarding the question above, it shouldn't be happening. If your spec finder is first in the meta_path and it returns a valid spec then the module is found, and remaining entries in sys.meta_path won't even be asked.

python (3.7) dataclass for self referenced structure [duplicate]

This question already has answers here:
How do I type hint a method with the type of the enclosing class?
(7 answers)
Closed 3 years ago.
class Node:
def append_child(self, node: Node):
if node != None:
self.first_child = node
self.child_nodes += [node]
How do I do node: Node? Because when I run it, it says name 'Node' is not defined.
Should I just remove the : Node and instance check it inside the function?
But then how could I access node's properties (which I would expect to be instance of Node class)?
I don't know how implement type casting in Python, BTW.
"self" references in type checking are typically done using strings:
class Node:
def append_child(self, node: 'Node'):
if node != None:
self.first_child = node
self.child_nodes += [node]
This is described in the "Forward references" section of PEP-0484.
Please note that this doesn't do any type-checking or casting. This is a type hint which python (normally) disregards completely1. However, third party tools (e.g. mypy), use type hints to do static analysis on your code and can generate errors before runtime.
Also, starting with python3.7, you can implicitly convert all of your type-hints to strings within a module by using the from __future__ import annotations (and in python4.0, this will be the default).
1The hints are introspectable -- So you could use them to build some kind of runtime checker using decorators or the like if you really wanted to, but python doesn't do this by default.
Python 3.7 and Python 4.03.10 onwards
PEP 563 introduced postponed evaluations, stored in __annotations__ as strings. A user can enable this through the __future__ directive:
from __future__ import annotations
This makes it possible to write:
class C:
a: C
def foo(self, b: C):
...
Starting in Python 3.10 (release planned 2021-10-04), this behaviour will be default.
Edit 2020-11-15: Originally it was announced to be mandatory starting in Python 4.0, but now it appears this will be default already in Python 3.10, which is expected 2021-10-04. This surprises me as it appears to be a violation of the promise in __future__ that this backward compatibility would not be broken until Python 4.0. Maybe the developers consider than 3.10 is 4.0, or maybe they have changed their mind. See also Why did __future__ MandatoryRelease for annotations change between 3.7 and 3.8?.
In Python > 3.7 you can use dataclass. You can also annotate dataclass.
In this particular example Node references itself and if you run it you will get
NameError: name 'Node' is not defined
To overcome this error you have to include:
from __future__ import annotations
It must be the first line in a module. In Python 4.0 and above you don't have to include annotations
from __future__ import annotations
from dataclasses import dataclass
#dataclass
class Node:
value: int
left: Node
right: Node
#property
def is_leaf(self) -> bool:
"""Check if node is a leaf"""
return not self.left and not self.right
Example:
node5 = Node(5, None, None)
node25 = Node(25, None, None)
node40 = Node(40, None, None)
node10 = Node(10, None, None)
# balanced tree
node30 = Node(30, node25, node40)
root = Node(20, node10, node30)
# unbalanced tree
node30 = Node(30, node5, node40)
root = Node(20, node10, node30)
If you just want an answer to the question, go read mgilson's answer.
mgilson's answer provides a good explanation of how you should work around this limitation of Python. But I think it's also important to have a good understanding of why this doesn't work, so I'm going to provide that explanation.
Python is a little different from other languages. In Python, there's really no such thing as a "declaration." As far as Python is concerned, code is just code. When you import a module, Python creates a new namespace (a place where global variables can live), and then executes each line of the module from top to bottom. def foo(args): code is just a compound statement that bundles a bunch of source code together into a function and binds that function to the name foo. Similarly, class Bar(bases): code creates a class, executes all of the code immediately (inside a separate namespace which holds any class-level variables that might be created by the code, particularly including methods created with def), and then binds that class to the name Bar. It has to execute the code immediately, because all of the methods need to be created immediately. Because the code gets executed before the name has been bound, you can't refer to the class at the top level of the code. It's perfectly fine to refer to the class inside of a method, however, because that code doesn't run until the method gets called.
(You might be wondering why we can't just bind the name first and then execute the code. It turns out that, because of the way Python implements classes, you have to know which methods exist up front, before you can even create the class object. It would be possible to create an empty class and then bind all of the methods to it one at a time with attribute assignment (and indeed, you can manually do this, by writing class Bar: pass and then doing def method1():...; Bar.method1 = method1 and so on), but this would result in a more complicated implementation, and be a little harder to conceptualize, so Python does not do this.)
To summarize in code:
class C:
C # NameError: C doesn't exist yet.
def method(self):
return C # This is fine. By the time the method gets called, C will exist.
C # This is fine; the class has been created by the time we hit this line.

From 'a' to Qt.Key_A in PyQt5

I'm sure it's basic, but I've searched and came back empty-handed.
I'm using Python 3.6.4 and PyQt5.
I want to store some custom action keys in a config file (via configparser), and then retrieve them and respond to that keypress event.
So basically I'm looking for a function in PyQt5 that performs the reverse of chr(Qt.Key_A) - from a character, returns a Qt.Key_.
I couldn't help myself with Googling this time, and PyQt5 is huge to peruse. I was wondering if someone could point me to the right direction.
I could use a dict, but I'm sure there must be a function that does it - I'm just not finding it.
My solution was to store the keys as ASCII code with ord(), since they can be directly compared to Qt.Key_ objects:
from PyQt5.QtCore import Qt
ord('A') == Qt.Key_A
Out[2]: True
If we are talking about alphanumeric keys only, getattr(Qt, f"Key_{key.upper()}" should work.
from PyQt5.QtCore import Qt
def string_to_key_converter(s):
attribute = f"Key_{s.upper()}"
if hasattr(Qt, attribute):
return getattr(Qt, attribute)
else:
raise ValueError(f"Key {s} is invalid or unsupported.")
> string_to_key_converter("a") is Qt.Key_A
>>> True

Is there a way to programmatically clear the terminal across platforms in Python 3?

Long-time lurker, first time asker.
Is there a way to automatically clear the terminal in Python 3 regardless of what platform the app is being used in?
I've come across the following (from this answer) which utilises ANSI escape codes:
import sys
sys.stderr.write("\x1b[2J\x1b[H")
But for it to work cross-platform it requires the colorama module which appears to only work on python 2.7.
For context I'm learning Python by building a game of battleships, but after each guess I want to be able to clear the screen and re-print the board.
Any help is appreciated!
Cheers
I use a single snippet for all the platforms:
import subprocess
clear = lambda: subprocess.call('cls' if os.name=='nt' else 'clear')
clear()
Same idea but with a spoon of syntactic sugar:
import subprocess
clear = lambda: subprocess.call('cls||clear', shell=True)
clear()
I know of this method
import os
clear = lambda: os.system('cls')
clear()
I'm not sure if it works with other platforms, but it's working in windows python 3.x
import os
clear = lambda: os.system('clear')
clear()
That might work for linux and OS X, but I can't test.

Resources