I'm having problems supporting python2 and python3 on a type() call. This demonstrates the problem:
from __future__ import unicode_literals
name='FooClass'
type(name, (dict,), {})
No problem on python3, but on python2:
Traceback (most recent call last):
File "test.py", line 6, in <module>
type(name, (dict,), {})
TypeError: type() argument 1 must be string, not unicode
This is related to Any gotchas using unicode_literals in Python 2.6?. In that question, someone recommends typecasting to a bytestring, so naively I thought about using six.b():
A “fake” bytes literal. data should always be a normal string literal.
In Python 2, b() returns a 8-bit string. In Python 3, data is encoded
with the latin-1 encoding to bytes.
So it looks like this:
from __future__ import unicode_literals
import six
name='FooClass'
type(six.b(name), (dict,), {})
But it fails on both python2 and python3:
$ python2 test.py
Traceback (most recent call last):
File "test.py", line 6, in <module>
type(six.b(name), (dict,), {})
TypeError: type() argument 1 must be string, not unicode
$ python3 test.py
Traceback (most recent call last):
File "test.py", line 6, in <module>
type(six.b(name), (dict,), {})
TypeError: type() argument 1 must be str, not bytes
So it seems that really, type() wants a python2 str which is a python3 bytestring on python2, but it wants a python3 str which is a python2 unicode string on python3.
What do you think ?
Is there something I don't understand ?
Or is there a real incompatibility with type() on python 2 and 3 ?
Isn't there any way to have the same type() call supporting both 2 and 3 ?
Shouldn't a tool like six provide a wrapper around type() in that case ?
six.b is written under the assumption that you won't use unicode_literals (and that you'll pass a string literal to it, as the documentation states), so the Python 2 implementation is just def b(s): return s as a Python 2 string literal is already a byte string.
Either don't use unicode_literals in this module, or use (as a comment suggests) str(name). In Python 3, that is a no-op. In Python 2, it silently converts the unicode string to a byte string (assuming some encoding that I can't be bothered to remember, but it's a superset of ASCII so you should be fine).
Related
Why doesn't Python's file write automatically call __str__?
$ cat person.py
class Person:
def __init__(self):
self.age = 22
def __str__(self):
return "my age is {}".format(self.age)
When I try to print it, everything goes fine, but writing Person to file fails:
>>> from person import Person
>>> dan = Person()
>>> print(dan)
my age is 22
>>> fl = open("dan.txt","wt")
>>> fl.write(dan)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: write() argument must be str, not Person
That's simply not how the API is designed.
From the definition of object.__str__(self):
Called by str(object) and the built-in functions format() and print() to compute the “informal” or nicely printable string representation of an object.
Beyond those explicitly listed instances, there's no reason to expect that implicit conversion will occur.
And from the definition of io.TextIOBase.write(s):
Write the string s to the stream and return the number of characters written.
So it's explicitly expecting a string, which is confirmed by the error message.
The simplest solution is to perform the conversion by using the str() function before passing it write() as an argument:
fl.write(str(dan))
When I try to print it, everything goes fine, but writing Person to file fails
Why not use print then?
print(dan, file=fl)
That will behave exactly like the print you are expecting (including a trailing newline), but write to a file instead of stdout.
Just trying to mess around and learn about python ctypes according to the official documentation at https://docs.python.org/3.8/library/ctypes.html
Everything works just fine until:
ValueError is raised when you call an stdcall function with the cdecl calling convention, or vice versa:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
quoted from official documentaion, while i get is:
>>> cdll.kernel32.GetModuleHandleA(None)
1374486528
>>> windll.msvcrt.printf(b"spam")
4
according to those MS documentation, seems these function calls work just fine
What's more, I also tried to mess around with the argument number so as to raise a ValueError, but that's what I get:
>>> cdll.kernel32.GetModuleHandleA(None,0,0)
1374486528
>>> windll.kernel32.GetModuleHandleA(0,0,0)
1374486528
>>> windll.kernel32.GetModuleHandleA()
0
>>> cdll.kernel32.GetModuleHandleA()
0
Seems the last two function calls does return null as there was an error, but no Value error exception.
The only error i got is OSError, just as the documentation example shows.
Can anyone explain this? I create virtual environment using conda and I test these codes both in python 3.6.12 and python 3.8.5.
And by the way ,according to the documentation: "ValueError is raised when you call an stdcall function with the cdecl calling convention, or vice versa", I wonder what exactly "call an stdcall function with cdecl calling convention" means? Maybe just by giving different number of arguments rather than the function required?
__stdcall and _cdecl have no difference on 64-bit compilers. There is only one calling convention and the notations are ignored and both WinDLL and CDLL work. 32-bit code is where it matters and the correct one must be used.
You should still use the appropriate WinDLL or CDLL in scripts if you want the script to work correctly on both 32-bit and 64-bit Python.
My 2.7.5 version of __future__.print_function doesn't allow use of the new argument:
>>> print('hi', end='')
Parsing error SyntaxError: invalid syntax (line 1)
I'll ask about why this is in a separate post if I can't figure that out. For now, I wanted to see what arguments were available in my environment's version of this function.
I looked at this SO post and some related ones, but these do not seem to work when I try:
>>> print.func_code.co_varnames
Parsing error SyntaxError: invalid syntax (line 1)
>>> print_function.func_code.co_varnames
Runtime error
Traceback (most recent call last):
File "<string>", line 1, in <module>
AttributeError: _Feature instance has no attribute 'func_code'
I'm guessing that the special nature of __future__ functions is why this standard technique fails.
Is there another way to check what args my version __future__.print_function takes?
You are trying to treat a built-in function (implemented in C) as a user-defined function. They are not the same thing. .func_code is only defined for user-defined functions (implemented in Python).
The __future__ module only holds metadata about features, the __future__.print_function object is not the same object as the print() function. Instead, the object tells you more about what version of Python first supported the feature, and in what version the feature becomes mandatory (and the from __future__ import becomes a no-op), as well as a bitfield flag for the compile() function:
>>> import __future__
>>> __future__.print_function
_Feature((2, 6, 0, 'alpha', 2), (3, 0, 0, 'alpha', 0), 65536)
>>> __future__.print_function.optional
(2, 6, 0, 'alpha', 2)
>>> __future__.print_function.mandatory
(3, 0, 0, 'alpha', 0)
>>> __future__.print_function.compiler_flag
65536
In Python 2.7, built-in function objects such as print() simply do not have enough information for you to discover what arguments they support. In Python 3, this is slowly changing as more and more built-in types are given metadata, but the print() function is not yet among them:
>>> import inspect
>>> inspect.signature(print)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mpietre/Development/Library/buildout.python/parts/opt/lib/python3.4/inspect.py", line 2045, in signature
return _signature_internal(obj)
File "/Users/mpietre/Development/Library/buildout.python/parts/opt/lib/python3.4/inspect.py", line 1947, in _signature_internal
skip_bound_arg=skip_bound_arg)
File "/Users/mpietre/Development/Library/buildout.python/parts/opt/lib/python3.4/inspect.py", line 1884, in _signature_from_builtin
raise ValueError("no signature found for builtin {!r}".format(func))
ValueError: no signature found for builtin <built-in function print>
I'm not sure where you got the idea from that new is a valid keyword for print() in any Python version however. No version of Python exists that supports that argument.
The only argument that is missing from print() in Python 2 and present in Python 3.3 and up, is the flush argument, see the Python 3 docs for print():
[...] if the flush keyword argument is true, the stream is forcibly flushed.
Changed in version 3.3: Added the flush keyword argument.
The only way to test for that (other than testing with sys.version_info >= (3, 3)) is to try and use it:
from io import StringIO
try:
print('', end='', flush=False, file=StringIO())
print_supports_flush = True
except TypeError:
print_supports_flush = False
print.__doc__ outputs:
SyntaxError: invalid syntax
where as
>>> getattr(__builtin__,"print").__doc__
Outputs:
print(value, ..., sep=' ', end='\n', file=sys.stdout)
Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments:
file : a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
Can anyone help me understand why print.__doc__ is giving a syntax error instead of printing the doc string
In Python 2 (or Python < 2.6 to be very exact) print is absolutely nothing like a function, and thus does not have docstring. It doesn't even evaluate all of its arguments before it starts printing:
>>> print 42, a
42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
42 was printed before the a was evaluated. print is a statement that expects 0 to N comma separated expression following it, optionally preceded by the construct >> file, the construct print.__doc__ is illegal. It makes as little sense as if.__doc__, or return.__doc__.
However starting with Python 2.6, the print function is available in the __builtin__ module, but is not used by default as the print statement collides with it, unless the parsing for print the statement is disabled by from __future__ import print_function.
Print isn't globally available as a function in Python 2, so you can't treat it as an object. It's a statement.
In Python 3, or Python 2 with from __future__ import print_function, however, print is a normal function and you can read the __doc__ attribute.
See: https://docs.python.org/2/library/functions.html#print
Note: This function is not normally available as a built-in since the name print is recognized as the print statement. To disable the statement and use the print() function, use this future statement at the top of your module:
from __future__ import print_function
Attempting to use the method argument as seen here yields the following error.
Python 3.2.3 (default, Sep 25 2013, 18:22:43)
>>> import urllib.request as r
>>> r.Request('http://example.com', method='POST')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'method'
>>>
No matter what/where I search, I can't seem to find a solution to my problem.
You're looking at the docs for Python 3.3 but running Python 3.2. In Python 3.2 the Request initializer doesn't have a method argument: http://docs.python.org/3.2/library/urllib.request.html#urllib.request.Request
FWIW depending on what kind of request you make (for example if the request includes a body) urllib will automatically use the appropriate method (i.e. POST). If you need to make a more specialized type of request such as HEAD you need to dig a little deeper. There are other answers on SO that help with that.