[issue43749] venv module does not copy the correct python exe

2021-12-10 Thread Eryk Sun


Eryk Sun  added the comment:

The Windows implementation of symlink_or_copy() actually copies "python.exe" 
and "pythonw.exe" launchers from "Lib/venv/scripts/nt". One cannot simply copy 
the "python3.exe" executable because the required DLLs aren't copied. If this 
seemed to work when testing, it was only because the installation directory was 
in PATH. The solution that actually works is to copy the launcher as 
"python3.exe".

--
nosy: +eryksun
resolution: fixed -> 
stage: resolved -> 
status: closed -> open

___
Python tracker 
<https://bugs.python.org/issue43749>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43749] venv module does not copy the correct python exe

2021-12-10 Thread Eryk Sun


Change by Eryk Sun :


--
components: +Windows
nosy: +paul.moore, steve.dower, tim.golden, zach.ware
versions: +Python 3.11 -Python 3.8

___
Python tracker 
<https://bugs.python.org/issue43749>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43749] venv module does not copy the correct python exe

2021-12-10 Thread Eryk Sun


Eryk Sun  added the comment:

The code to copy a file could be rewritten to use a regex match. For example:

# Copy src to dst. If src is a base executable, copy a launcher.
dirname, filename = os.path.split(src)
m = re.match(r'(pythonw?)[0-9._]*((?:_d)?(?:\.exe|\.pdb))$',
filename.lower())
if m is not None:
src = os.path.join(os.path.dirname(__file__),
'scripts', 'nt', m.group(1) + m.group(2))
# If the base environment is a Python source build, use
# the launcher in the build directory instead of
# "Lib/venv/scripts/nt".
if sysconfig.is_python_build(True) or not os.path.isfile(src):
basename = ('venvlauncher' if m.group(1) == 'python' else
'venvwlauncher')
src = os.path.join(dirname, basename + m.group(2))

--

___
Python tracker 
<https://bugs.python.org/issue43749>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue23948] Deprecate os.kill() on Windows

2021-12-11 Thread Eryk Sun


Change by Eryk Sun :


--
resolution:  -> rejected
stage: needs patch -> resolved
status: open -> closed

___
Python tracker 
<https://bugs.python.org/issue23948>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue42962] Windows: SystemError during os.kill(..., signal.CTRL_C_EVENT)

2021-12-11 Thread Eryk Sun


Change by Eryk Sun :


--
resolution:  -> duplicate
stage:  -> resolved
status: open -> closed
superseder:  -> missing return in win32_kill?

___
Python tracker 
<https://bugs.python.org/issue42962>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue14484] missing return in win32_kill?

2021-12-11 Thread Eryk Sun


Eryk Sun  added the comment:

The details of os.kill() on Windows have been discussed extensively for years 
in various issues such as bpo-26350, bp-23948, and bp42962. But the problem of 
the missing return statement is always overwhelmed by discussion of the 
egregiously bad design of this function and its misuse, which depends on bugs 
in WinAPI GenerateConsoleCtrlEvent() when passed a PID that is not a process 
group ID and/or not attached to the console.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue14484>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue14484] missing return in win32_kill?

2021-12-11 Thread Eryk Sun


Change by Eryk Sun :


--
Removed message: https://bugs.python.org/msg408336

___
Python tracker 
<https://bugs.python.org/issue14484>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue14484] missing return in win32_kill?

2021-12-11 Thread Eryk Sun


Eryk Sun  added the comment:

The details of os.kill() on Windows have been discussed extensively for years 
in various issues such as bpo-26350, bpo-23948, and bpo-42962. But the problem 
of the missing return statement is always overwhelmed by discussion of the 
egregiously bad design of this function and its misuse, which depends on bugs 
in WinAPI GenerateConsoleCtrlEvent() when passed a PID that is not a process 
group ID and/or not attached to the console.

--
versions: +Python 3.7 -Python 3.10, Python 3.11, Python 3.9

___
Python tracker 
<https://bugs.python.org/issue14484>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue37701] shutil.copyfile raises SpecialFileError for symlink to fifo

2021-12-11 Thread Eryk Sun


Eryk Sun  added the comment:

> Raising a SpecialFileError would be OK if `follow_symlinks` was False.

I expect it to fail if follow_symlinks is True, which is the default value. I 
expect it to succeed with follow_symlinks=False, which should create a shallow 
copy of just the symlink, regardless of its target. Instead, what happens is 
that it calls shutil._stat(fn) on both src and dst, regardless of 
follow_symlinks. I think the call should be shutil._stat(fn, follow_symlinks). 
This requires updating shutil._stat() to pass the value to fn.stat() and 
os.stat().

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue37701>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46052] IDLE: make Ctrl, Alt + IME non-ascii letter work on Windows

2021-12-12 Thread Eryk Sun


Eryk Sun  added the comment:

The alternate keyboard shortcuts for the clipboard work fine with a Russian 
keyboard layout:

   copy: Ctrl+Insert
cut: Shift+Delete
  paste: Shift+Insert

Some programs also support Alt+Backspace for undo (Ctrl+Z). Watch out with 
Shift+Delete in GUI shells, since it physically deletes files, bypassing the 
recycle bin (trash), instead of cutting the selected files to the clipboard.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46052>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46052] IDLE: make Ctrl, Alt + IME non-ascii letter work on Windows

2021-12-12 Thread Eryk Sun

Eryk Sun  added the comment:

I think the following wiki article still applies even though it was first 
discussed in 2003: "KeySyms on platforms other than X11" [1]. In particular, it 
states the following:

On Windows and MacOS X Tk only supports keysyms correctly for a 
limited number of keys, namely special keys, and the ranges of ASCII
and ISO-8859-1 (support of ISO-8859-1 on MacOS X since 8.4.2).

I can confirm the result for the Russian keyboard mapping. The value of keysym 
is "??", even when combined with the control key. 

x:
char: 'ч', ord: 0447, code: 0058, sym: '??', num: 0447.
Ctrl+x:
char: '\x18', ord: 0018, code: 0058, sym: '??', num: 0447.
c:
char: 'с', ord: 0441, code: 0043, sym: '??', num: 0441.
Ctrl+c:
char: '\x03', ord: 0003, code: 0043, sym: '??', num: 0441.
v:
char: 'м', ord: 043c, code: 0056, sym: '??', num: 043c.
Crl+v:
char: '\x16', ord: 0016, code: 0056, sym: '??', num: 043c.

(I modified the keyevent function from msg408400 to use hexadecimal and repr 
formatting.)

I checked Ubuntu 20.04 with a Russian keyboard layout. It seems in Linux the 
combination with the control key changes keysym and keysym_num to use the ASCII 
characters "x", "c", and "v": 

x:
char: 'ч', ord: 0447, code: 0035, sym: 'Cyrillic_che', num: 06de.
Ctrl+x:
char: '\x18', ord: 0018, code: 0035, sym: 'x', num: 0078.
c:
char: 'с', ord: 0441, code: 0036, sym: 'Cyrillic_es', num: 06d3.
Ctrl+c:
char: '\x03', ord: 0003, code: 0036, sym: 'c', num: 0063.
v:
char: 'м', ord: 043c, code: 0037, sym: 'Cyrillic_em', num: 06cd.
Crl+v:
char: '\x16', ord: 0016, code: 0037, sym: 'v', num: 0076.

---
[1] https://wiki.tcl-lang.org/page/KeySyms+on+platforms+other+than+X11

--

___
Python tracker 
<https://bugs.python.org/issue46052>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46056] Cannot use virtual environment on Windows 10 in corporate security settings

2021-12-12 Thread Eryk Sun


Eryk Sun  added the comment:

Try using symlinks if you're allowed, e.g. `python -m venv --symlinks `.

Note that a virtual environment created with symlinks is unreliable in some 
cases because ShellExecute[Ex]W() eagerly resolves a symlink before calling 
CreateProcessW().

Also, you won't be able to use EXE script wrappers in an active environment due 
to the security restrictions in place on your system. You'll need to use 
`python -m ` alternative commands, such as `python -m pip` instead of 
`pip`.

--
components:  -Windows
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46056>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46056] Cannot use virtual environment on Windows 10 in corporate security settings

2021-12-13 Thread Eryk Sun


Eryk Sun  added the comment:

> symlinks do not work for me

Sorry, I forgot that you're using the store app. The store app has to use the 
copied venv launchers. 

When a store app is run from the command line, the system executes an appexec 
link from "%LocalAppData%\Microsoft\WindowsApps". Unfortunately appexec links 
have to be executed directly in order for CreateProcessW() to find the required 
app information that's in the link. The API doesn't manually reparse symlinks 
until it reaches an appexec link.

--

___
Python tracker 
<https://bugs.python.org/issue46056>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45915] Use fcntl(fd, F_GETFD) to check whether an fd is valid

2021-12-13 Thread Eryk Sun


Eryk Sun  added the comment:

PR 29821 adds __APPLE__ to the platforms that use fcntl(fd, F_GETFD). Is this 
okay on macOS, given bpo-30225? Apparently fstat() fails if the other end of a 
pipe is closed.

--

___
Python tracker 
<https://bugs.python.org/issue45915>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46084] Python 3.9.6 scan_dir returns filenotfound on long paths, but os_walk does not

2021-12-15 Thread Eryk Sun


Eryk Sun  added the comment:

> Python 3.9.6 scan_dir returns filenotfound on long paths, 
> but os_walk does not.

This would be surprising. os.walk() has been implemented via os.scandir() since 
Python 3.5. Do you have a concrete example of the directory structure to test? 

> I see that many people on the internet have said to 
> change the working directory as a work around.  

Changing the working directory is a workaround in Unix, not Windows. Without 
long-path support, the working directory in Windows is limited to 258 (MAX_PATH 
- 2) characters.

Without long-path support, the workaround in Windows is to use an extended 
path, i.e. a Unicode path that's fully-qualified and normalized -- as returned 
by os.path.abspath() -- and prefixed by "?\\" or "?\\UNC\\" (e.g. 
r"\\?\C:\spam" or r"\\?\UNC\server\share\spam"). This allows the native path 
length limit of about 32760 characters. Some API functions and applications do 
not support extended paths. In particular setting an extended path as the 
working directory is unsupported and buggy, even if long-path support is 
enabled. But extended paths work fine with most file functions in the os and 
shutil modules, such as os.scandir(), os.stat(), os.open(), shutil.copytree(), 
and shutil.rmtree().

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46084>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46084] Python 3.9.6 scan_dir returns filenotfound on long paths, but os_walk does not

2021-12-15 Thread Eryk Sun


Eryk Sun  added the comment:

It works as expected for me:

>>> len(p)
261
>>> print(p)
C:\Temp\Jim\Documents\jschw_uiowtv3_old\AppData\Local\Google\Chrome\User 
Data\Default\Extensions\nenlahapcbofgnanklpelkaejcehkggg\0.1.823.675_0\notifications\pages\Cashback\components\CashBackResolve\components\RewardsActivation\components\CashbackSectionSimple

os.walk() can list the files in directory `p` if the \\?\ prefix is prepended:

>>> next(os.walk('?\\' + p))[-1]
['spam.txt']

Without the prefix, the internal os.scandir() call fails, but by default the 
error is ignored:

>>> next(os.walk(p))[-1]
Traceback (most recent call last):
  File "", line 1, in 
StopIteration

We can print the exception to see that it's the expected ERROR_PATH_NOT_FOUND 
(3) error for a path that's too long:

>>> next(os.walk(p, onerror=print))[-1]
[WinError 3] The system cannot find the path specified: 
'C:\\Temp\\Jim\\Documents\\jschw_uiowtv3_old\\AppData\\Local\\Google\\Chrome\\User
 
Data\\Default\\Extensions\\nenlahapcbofgnanklpelkaejcehkggg\\0.1.823.675_0\\notifications\\pages\\Cashback\\components\\CashBackResolve\\components\\RewardsActivation\\components\\CashbackSectionSimple'
Traceback (most recent call last):
  File "", line 1, in 
StopIteration

--

___
Python tracker 
<https://bugs.python.org/issue46084>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46084] Python 3.9.6 scan_dir returns filenotfound on long paths, but os_walk does not

2021-12-15 Thread Eryk Sun


Eryk Sun  added the comment:

If I had long paths enabled, then next(os.walk(p, onerror=print)) would not 
have printed the error that I showed in the example and would not have 
immediately raised StopIteration. Instead it would have returned a (dirpath, 
dirnames, filenames) result for directory `p`. Did you repeat the simple 
examples that I showed, exactly as shown, and get a different result?

--

___
Python tracker 
<https://bugs.python.org/issue46084>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46084] Python 3.9.6 scan_dir returns filenotfound on long paths, but os_walk does not

2021-12-15 Thread Eryk Sun


Eryk Sun  added the comment:

> but errors in DirEntry.is_dir() and DirEntry.is_symlink() 
> are always ignored

In Windows, is_symlink() won't fail due to a long path, since that information 
comes from the directory listing, but is_dir() might fail for a long path if 
it's a symlink to a directory. Windows requires that a symlink to a directory 
is also a directory (i.e. the symlink reparse point is set on an empty 
directory), but it's not enough to check that it's a directory symlink. 
is_dir() requires checking that the target exists, which may fail if the path 
of the link is too long to open and resolve the link target.

--

___
Python tracker 
<https://bugs.python.org/issue46084>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46143] [docs] IO > Text Encoding info outdated

2021-12-21 Thread Eryk Sun


Eryk Sun  added the comment:

The rare circumstance in which UTF-8 mode gets enabled automatically is 
described in the following paragraph [1]:

If the PYTHONUTF8 environment variable is not set at all, then the
interpreter defaults to using the current locale settings, unless the
current locale is identified as a legacy ASCII-based locale (as
described for PYTHONCOERCECLOCALE), and locale coercion is either 
disabled or fails. In such legacy locales, the interpreter will
default to enabling UTF-8 mode unless explicitly instructed not to do
so.

Note that UTF-8 mode is never enabled automatically in Windows. In contrast to 
POSIX, the locale encoding in Windows is unrelated to the current LC_CTYPE 
locale. Instead, the locale encoding gets set to the process code page, which 
is based on the system locale by default and never changes while a process is 
running. The system locale may be incompatible with the current LC_CTYPE 
locale, Windows user locale, and preferred UI language (e.g. for text resources 
such as error messages), so try to explicitly use UTF-8 for text files whenever 
possible.

---
[1] https://docs.python.org/3/library/os.html#utf8-mode

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46143>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41222] POpen bufsize=0 ignored with universal_newlines=True

2021-12-22 Thread Eryk Sun


Eryk Sun  added the comment:

> Buffering is necessary for implementing the universal_newlines 

Why is that? I can see that it requires newline state tracking, and the 
allowance to make two read(fd, &c, 1) system calls for a single read(1) method 
call, in case a "\n" has to be ignored.

testproc-unbuffered.py runs to completion in 3.11 if the following statement 
that changes the text wrapper's chunk size is added right after creating the 
Popen() instance:

if sys.version_info[0] > 2:
process.stdout._CHUNK_SIZE = 1

The initial chunk size for a text wrapper is hard coded as 8192 bytes. For some 
reason the constructor has no parameter for it.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue41222>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46153] function fails in exec when locals is given

2021-12-22 Thread Eryk Sun


Eryk Sun  added the comment:

> If exec gets two separate objects as globals and locals, 
> the code will be executed as if it were embedded in a 
> class definition.

That's a misleading comparison because a class definition intentionally 
supports nonlocal closures, which exec() doesn't support and shouldn't support. 
For example:

a = 1

def f():
a = 2
class C:
print(a)

def g():
a = 2
class C:
nonlocal a
a = 3
print(a)

>>> f()
2
>>> g()
3

exec() executes as module code. Using separate globals and locals mappings 
doesn't magically change how the code is compiled and executed to make it 
equivalent to a class definition. To understand the case of separate globals 
and locals, just remember that assigning to a variable by default makes it a 
local variable, unless it's declared as a global. Also, class and function 
definitions are implicitly an assignment, which by default will be local.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46153>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46153] function fails in exec when locals is given

2021-12-22 Thread Eryk Sun


Eryk Sun  added the comment:

> That's taken straight out of the documentation.

Yes, but it's still a misleading comparison.

> Until I understood that exec with two different mapping objects as 
> globals and locals behaves as if the code where embedded inside a 
> class, I found the reported behaviour totally perplexing.

The basic execution model of Python is that a frame that executes with 
non-optimized locals -- in module and class definitions -- can use the same 
mapping for globals and locals. Indeed, that's how the interpreter executes 
modules. However, exec() is generalized to allow executing module code with 
separate globals and locals. 

Saying that code will be "executed as if it were embedded in a class 
definition" is correct only so far as the fact that globals and locals are 
different in this case. But it's also misleading because the code gets compiled 
as module-level code, not as class code.

It should be pretty obvious why the following fails:

exec("a = 1\ndef f(): return a\nprint(f())", {}, {})

Assignment is local by default, unless otherwise declared. Function f() has no 
access to the local scope where `a` is defined because Python doesn't support 
closures over non-optimized locals, particularly because we emphatically do not 
want that behavior for class definitions. 

It should be equally clear why the following succeeds:

exec("global a\na = 1\ndef f(): return a\nprint(f())", {}, {})

> because a class definition intentionally supports nonlocal closures, 
>
>I don't know what you mean by that. Classes are never closures. Only 
>functions can be closures.

I didn't say that a class can be a closure. That's never the case because a 
class uses non-optimized locals. But a class definition does support free 
variables that are bound to an enclosing scope. exec() does not support this, 
so the exact same code can execute differently in the context of a class 
definition.

> It is equivalent to code executed inside a class scope.

That depends on the code and the context. Please refer to my first example in 
comparison to the following:

a = 1
def f():
a = 2
exec('print(a)', globals(), {})

>>> f()
1

It's different behavior for print(a) because both exec() and compile(source, 
filename, 'exec') produce module code, not class code. The free variable `a` 
gets bound to the global scope for the exec() example, while for the class 
definition free variable `a` is bound to the local `a` in the frame of the 
function call.

To implement this different behavior, the code object for a class definition 
uses bytecode operations such as COPY_FREE_VARS and LOAD_CLASSDEREF, which are 
never used for module-level code. For example, from the original example, 
here's the class definition code:

>>> dis.dis(f.__code__.co_consts[2])
  0 COPY_FREE_VARS   1
  2 LOAD_NAME0 (__name__)
  4 STORE_NAME   1 (__module__)
  6 LOAD_CONST   0 ('f..C')
  8 STORE_NAME   2 (__qualname__)

  4  10 LOAD_NAME3 (print)
 12 LOAD_CLASSDEREF  0 (a)
 14 CALL_FUNCTION1
 16 POP_TOP
 18 LOAD_CONST   1 (None)
 20 RETURN_VALUE

--

___
Python tracker 
<https://bugs.python.org/issue46153>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46153] function fails in exec when locals is given

2021-12-23 Thread Eryk Sun


Eryk Sun  added the comment:

> You seem to be arguing that a description in the docs is "misleading", 
> not because it misleads, but because it don't describe a situation 
> which has nothing to do with the situation that the docs are describing.

To me it's misleading to say "the code will be executed as if it were embedded 
in a class definition" because that is not always the case. The example with 
print(a) shows that. One can take it another level to compare function 
definitions in a class definition compared to exec(). A function defined in an 
exec() is not compiled to bind its free variables to the outer lexical scope in 
the context of the exec() call, while a function defined in a class definition 
does. For example:

class:

def f():
   a = 2
   class C:
   def g(): print(a)
   return C.g

>>> a = 1
>>> g = f()
>>> g()
2

exec():

def f():
   a = 2
   l = {}
   exec('def g(): print(a)', globals(), l)
   return l['g']

>>> a = 1
>>> g = f()
>>> g()
1

You asked what I would say in its place, but I don't have a simple answer that 
can take the place of the one-liner in the docs. Here's something, but I'm sure 
you won't be happy with it:

The code will be executed in a manner that's similar to a class definition with 
regard to the use of separate locals and globals scopes. However, there can be 
significant differences in certain contexts with regard to how the same code is 
compiled for an exec() call compared to a class definition. In particular, code 
in a class definition is compiled to bind its free variables to the lexical 
scopes of outer function calls in the defining context, which isn't possible 
with exec(). Also, the top-level code in a class definition supports `nonlocal` 
declarations, which is a syntax error with exec().

--

___
Python tracker 
<https://bugs.python.org/issue46153>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46171] venv module produces spurious warning that location has moved

2021-12-24 Thread Eryk Sun


Eryk Sun  added the comment:

There's no point to making the user worry about short names, symlinks, or 
non-canonical mount points in the filesystem path of a virtual environment. 
It's still accessible where the user expects to find it. The problem for 
bpo-45337 is filesystem redirection in UWP apps, in particular for files and 
directories created under "%USERPROFILE%\AppData". The warning could be limited 
to just paths in that tree.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46171>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46171] venv module produces spurious warning that location has moved

2021-12-24 Thread Eryk Sun


Eryk Sun  added the comment:

There's still the problem of the system using short names in %TEMP%, since by 
default that's under "%USERPROFILE%\AppData". Either an exception could be made 
for the temp directory, since it's never redirected (AFAIK), or support could 
be added for GetLongPathNameW() [1] as nt._getlongpathname().

---
[1] 
https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlongpathnamew

--

___
Python tracker 
<https://bugs.python.org/issue46171>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46171] venv module produces spurious warning that location has moved

2021-12-28 Thread Eryk Sun


Eryk Sun  added the comment:

> There are plenty of other ways to get a venv through a potentially 
> unexpected path (turns out I've been doing one for years)

Examples would be appreciated because I'm drawing a blank here. A junction or 
directory symlink in the parent path shouldn't be a problem. Otherwise I'd 
prefer that the solution for bpo-45337 was limited to an app container and 
paths in "%USERPROFILE%\AppData" (excluding "%TEMP%"). An app container can be 
detected via the package family name, if any, as returned by 
GetCurrentPackageFamilyName().

--

___
Python tracker 
<https://bugs.python.org/issue46171>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46193] Using a dictionary for open files.

2021-12-29 Thread Eryk Sun


Eryk Sun  added the comment:

Every time open(r"test.txt", "w") is called, the existing "test.txt" file gets 
overwritten in the filesystem as an empty file. For each iteration, 
setdefault() returns a reference to the first file object, which advances its 
file pointer with each fh.write("\nHello\n") call. For the last write, the file 
pointer is at offset 693 (i.e. 7 * 99), and the OS first writes null bytes up 
to the file pointer. This is normal behavior, not a bug.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46193>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46171] venv module produces spurious warning that location has moved

2021-12-31 Thread Eryk Sun


Eryk Sun  added the comment:

> My VHDX mounted in a directory is affected by this

I created a VHDX and mounted it in a directory. It's just a regular volume 
mount point with a junction (IO_REPARSE_TAG_MOUNT_POINT). That won't cause any 
problems, so I guess your setup must be different in some way.

> I suspect (but haven't tested) that some file sharing handlers or 
> attached devices could also hit it - thinking here of IoT devices
> that provide a filesystem-like interface.

My takeaway is that because filesystem filter drivers on some systems may 
arbitrarily redirect some paths in some cases, and redirect differently or not 
at all in some other context, then we need to always log a warning showing the 
real path and always embed the real path in pip.exe. The only clear example we 
have is bpo-45337, but we're erring on the side of caution instead of targeting 
the warning at the one known case. The generic approach happens to also include 
any common use of directory symbolic links or junctions, but it's not practical 
to handle them differently. You're open to special casing short DOS names, 
however, e.g. by comparing the real path to the long path name from 
GetLongPathNameW().

Note that if the user continues to use the path with a reparse point or 
redirection to access the virtual environment (if possible; it's not for 
bpo-45337), then running `python -m pip` will install scripts or executable 
script wrappers that refer to the accessed path, not the real path. pip uses 
distlib for entrypoint scripts. Apparently distlib doesn't resolve the real 
path of sys.executable when creating the script shebang.

For POSIX, including macOS, I don't know the range of possibilities for 
dependent filesystem redirection. Bind mounts (i.e. mounting an arbitrary path 
on a directory) can be an issue since they're temporary unless configured in 
/etc/fstab. However, we can't do anything to warn about them because POSIX 
realpath() doesn't resolve them.

> warning that can be explained in exactly the same way on every platform

I guess you mean every edition of Windows.

--

___
Python tracker 
<https://bugs.python.org/issue46171>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue34931] os.path.splitext with more dots

2022-01-03 Thread Eryk Sun


Eryk Sun  added the comment:

> On Windows the extension of "python.exe" is "exe", not ".exe". 

FWIW, a file extension in Windows includes the dot. Trailing dots are stripped 
from filenames, so a file can't be named "python." because it's the same as 
just "python". (It's possible to prevent stripping trailing dots from a name by 
using a \\?\ literal path, but creating such a filename is a bad idea.) 

The shell API's file associations include the dot in the file extension. Also, 
the PATHEXT environment variable (i.e. the list of extensions that a CLI shell 
should try appending in a PATH search) includes the dot in each extension. In 
both of the latter cases, an extension that's just "." matches a filename that 
has no extension. In other words, the "." file extension can be used to 
associate files that have no extension with a ProgID (i.e. a programmatic 
identifier, which defines properties and actions for a file type), and adding 
"." to PATHEXT includes files that have no extension in a PATH search.

To clarify further, here are some results from PathCchFindExtension() [1]:

import ctypes
path = ctypes.OleDLL('api-ms-win-core-path-l1-1-0')
s = (ctypes.c_wchar * 100)()
ext = ctypes.c_wchar_p()

>>> s.value = 'python.exe'
>>> _ = path.PathCchFindExtension(s, len(s), ctypes.byref(ext))
>>> ext.value
'.exe'

>>> s.value = '...exe'
>>> _ = path.PathCchFindExtension(s, len(s), ctypes.byref(ext))
>>> ext.value
'.exe'

>>> s.value = 'python.'
>>> _ = path.PathCchFindExtension(s, len(s), ctypes.byref(ext))
>>> ext.value
'.'

---
[1] 
https://docs.microsoft.com/en-us/windows/win32/api/pathcch/nf-pathcch-pathcchfindextension

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue34931>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46217] 3.11 build failure on Win10: new _freeze_module changes?

2022-01-03 Thread Eryk Sun


Eryk Sun  added the comment:

With just PATHCCH_ALLOW_LONG_PATHS, PathCchCombineEx() returns a long path as a 
normal path (i.e. not a \\?\ extended path) if long-path support is enabled for 
the current process. Otherwise, if long-path support isn't enabled or 
available, as is always the case in Windows 8.1, then PATHCCH_ALLOW_LONG_PATHS 
makes PathCchCombineEx() return a long path as an extended path. In most cases, 
extended paths work fine. A common exception is that extended paths aren't 
supported for the working directory.

In Windows 10+, PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS forces 
PathCchCombineEx() to always return a long path as a normal path, even if 
long-path support is disabled for the current process. For most cases, this 
option pretty much guarantees that long paths will cause immediate failures if 
long-path support is disabled for the current process. In some cases one might 
want an immediate failure, instead of messing around with extended paths that 
might fail in some obscure, buggy way. Also, for pure path manipulation one may 
not care whether the current process can access the path without the \\?\ 
prefix.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46217>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46276] ImportError: DLL load failed while importing

2022-01-06 Thread Eryk Sun


Eryk Sun  added the comment:

"_frida.cp310-win_amd64.pyd" is a bad build or corrupted file. It imports a 
procedure with no name from a DLL with no name, which causes the loader to 
search for a file named ".DLL". Even if that were found, it imports another 
procedure with no name or correct ordinal from "IPHLPAPI.DLL". There are 
probably more problems with this DLL.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46276>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46276] ImportError: DLL load failed while importing

2022-01-06 Thread Eryk Sun

Eryk Sun  added the comment:

> ImportError: DLL load failed while importing _socket: El parámetro no es 
> correcto.

I'm not familiar with the implementation of PyInstaller, which is a third-party 
tool. The above invalid-parameter error may be indirectly related to the 
problem with frida, or it may be a separate issue. 

The problem with frida is simply that "_frida.cp310-win_amd64.pyd" is an 
invalid or corrupted DLL that the system can't load. I don't know what went 
wrong with their build and distribution process that caused the problem. It's 
something to resolve on the frida issue tracker.

--

___
Python tracker 
<https://bugs.python.org/issue46276>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue9937] _winreg.EnumValue causes MemoryError

2022-01-07 Thread Eryk Sun


Eryk Sun  added the comment:

> Did anyone reproduce on python3.X? 

In principle, winreg should not have a problem in 3.x because it only uses the 
UTF-16 wide-character API.

--
nosy: +eryksun
stage:  -> resolved
status: pending -> closed

___
Python tracker 
<https://bugs.python.org/issue9937>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46217] 3.11 build failure on Win10: new _freeze_module changes?

2022-01-07 Thread Eryk Sun


Eryk Sun  added the comment:

Terry, it's just a simple bug that slipped by. The PATHCCH_OPTIONS enum in the 
SDK header "PathCch.h" unconditionally defines all of the options. So there's 
no compiler error when building with a newer version of the SDK, even though we 
define _WIN32_WINNT to 0x602 (Windows 8).

--
nosy:  -pablogsal

___
Python tracker 
<https://bugs.python.org/issue46217>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46297] Python interpreter crashes on bootup with multiple PythonPaths set in registry

2022-01-07 Thread Eryk Sun


Change by Eryk Sun :


--
components: +Windows
nosy: +paul.moore, tim.golden, zach.ware
type: crash -> behavior

___
Python tracker 
<https://bugs.python.org/issue46297>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46297] Python interpreter crashes on bootup with multiple PythonPaths set in registry

2022-01-07 Thread Eryk Sun


Change by Eryk Sun :


--
type: behavior -> crash

___
Python tracker 
<https://bugs.python.org/issue46297>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46287] UNC path normalisation issues on Windows

2022-01-07 Thread Eryk Sun


Eryk Sun  added the comment:

> _Py_abspath/_getfullpathname does not always call GetFullPathNameW on 3.11.

Also, PathCchSkipRoot() doesn't recognize forward slash as a path separator, so 
_Py_isabs() is wrong in many cases compared to the same path that uses 
backslash as the path separator.

For example, _Py_isabs() wrongly returns false in the following cases, so 
GetFullPathNameW() is called, and ironically the misbehavior of _Py_isabs() 
leads to the correct result.

>>> os.path.abspath('//spam//eggs. . .')
'spam\\eggs'
>>> os.path.abspath('C:/spam. . .')
'C:\\spam'
>>> os.path.abspath('C:/nul')
'.\\nul'

_Py_isabs() returns true in the following cases, so only normpath() is called:

>>> os.path.abspath(r'\\spam\\eggs. . .')
'spameggs. . .'
>>> os.path.abspath('C:\\spam. . .')
'C:\\spam. . .'
>>> os.path.abspath('C:\\nul')
'C:\\nul'

As the above shows, normpath() doesn't remove trailing dots and spaces from the 
last component of a path, and it doesn't special case DOS devices in the last 
component of a drive-letter path. The latter is still implemented for the NUL 
device in Windows 11 and implemented for all DOS devices in Windows 8.1 and 10 
(e.g. CON, CONIN$, CONOUT$, AUX, PRN, COM<1-9>, LPT<1-9>).

I would prefer to remove the _Py_isabs() check from _Py_abspath() in Windows, 
unless there's a strong case for startup performance, in which case I'd prefer 
to revert the change to nt._getfullpathname() and only use _Py_abspath() during 
startup.

--

___
Python tracker 
<https://bugs.python.org/issue46287>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46226] User specific paths added to System PATH environment variable

2022-01-07 Thread Eryk Sun


Eryk Sun  added the comment:

Were you upgrading an existing installation of Python 3.10? Did it actually 
install in "C:\Program Files\Python310"? Is Python currently installed in 
per-user "%LocalAppData%\Programs\Python\Python310"? Was it ever installed 
there?

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46226>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46142] python --help output is too long

2022-01-10 Thread Eryk Sun


Eryk Sun  added the comment:

> Do you know other projects which dump the full help into stdout 
> when asking for the "full help"?

`ps --help` lists sections "". The "--help 
all" output is all sections, but truly all help is only available via `man ps`.

> For me, the best CLI is "git help", "git help init", etc. "git help 
> init" opens "man git-init". I don't know its behaviour on platforms 
> without manpage support, like Windows.

With Git for Windows, `git help ` opens an HTML formatted manual page 
in a web browser. It should use a new tab in an existing browser window.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46142>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46326] 'virtualenv --clear' should prompt user before nuking entire directory

2022-01-10 Thread Eryk Sun


Eryk Sun  added the comment:

Ned, the CLI of venv has a `--clear` option, which invokes EnvBuilder [1] with 
clear=True. It's implemented by EnvBuilder.clear_directory(). Ali wants this 
method to prompt for confirmation before deleting the directory contents and 
also proposes the addition of a new `--force` option to skip the prompt.

---
[1] https://docs.python.org/3/library/venv.html#api

--
nosy: +eryksun
status: pending -> open

___
Python tracker 
<https://bugs.python.org/issue46326>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46326] 'virtualenv --clear' should prompt user before nuking entire directory

2022-01-10 Thread Eryk Sun

Eryk Sun  added the comment:

The implementation of `--clear` in virtualenv apparently was changed to align 
with venv. On the linked virtualenv issue, Bernát Gábor suggested creating a 
bpo issue for venv to determine how the issue should be resolved for virtualenv.

--
nosy: +vinay.sajip

___
Python tracker 
<https://bugs.python.org/issue46326>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46287] UNC path normalisation issues on Windows

2022-01-14 Thread Eryk Sun


Change by Eryk Sun :


--
Removed message: https://bugs.python.org/msg410068

___
Python tracker 
<https://bugs.python.org/issue46287>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45554] multiprocessing exitcode is insufficiently documented

2022-01-18 Thread Eryk Sun


Eryk Sun  added the comment:

PR 30142 is sufficient for Unix, but it's missing a bit for Windows.

In Windows, the C runtime library maps the console event for Ctrl+Break to 
SIGBREAK. The default handler for SIGBREAK exits with 0xC13A (i.e. 
STATUS_CONTROL_C_EXIT). This value is unrelated to the value of signal.SIGBREAK 
(21) or signal.SIGINT (2).

To forcefully terminate a process, Windows taskkill and Task Manager, and 
pretty much all utilities that can kill a process, call TerminateProcess() with 
1 as the exit status. There's no way to know that this is a forced termination 
as opposed to an unhandled error.

The internal terminate() method in Windows uses 0x1 as the real exit status 
of the process, but the internal wait() method maps this to -signal.SIGTERM 
(-15). Thus the case of calling Process.terminate(), and only this case, is 
faked in Windows to look like the process was killed by a signal.

--
nosy: +eryksun
resolution: fixed -> 
stage: resolved -> patch review
status: closed -> open

___
Python tracker 
<https://bugs.python.org/issue45554>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45554] multiprocessing exitcode is insufficiently documented

2022-01-18 Thread Eryk Sun


Change by Eryk Sun :


--
stage: patch review -> resolved
status: open -> closed

___
Python tracker 
<https://bugs.python.org/issue45554>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46454] '0 -> /dev/null' is lost

2022-01-22 Thread Eryk Sun


Eryk Sun  added the comment:

> If some one closes fd 0, then he reopens it. it will not be inherited.

In Windows, when a console process spawns a child console process without 
enabling handle inheritance -- e.g. subprocess.Popen(['python.exe']) -- the OS 
will manually duplicate (not inherit) the standard handles to the child. It 
doesn't matter in this case that a standard handle isn't inheritable. 

The latter also doesn't matter when any of the standard handles is overridden 
by a Popen() call in Windows -- e.g. subprocess.Popen(['python.exe'], 
stderr=DEVNULL). All of the standard handle values have to be overridden 
together. Popen() uses this as an opportunity to duplicate an inheritable 
handle for each that's not overridden, instead of just copying the handle value.

On the other hand, if subprocess.Popen() is called in Windows with handle 
inheritance enabled and without overriding the standard handles -- e.g. 
subprocess.Popen(['python.exe'], close_fds=False) -- then the standard handles 
should be inheritable. If any of the standard handles isn't inheritable, then 
at best the standard handle value in the child will be invalid or used by a 
handle for a kernel object type other than a file (e.g. process, event). By 
coincidence, however, a file open in the child could reuse the standard handle 
value, which can lead to buggy behavior.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46454>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46490] Add "follow_symlinks=False" support for "os.utime()" on Windows

2022-01-23 Thread Eryk Sun


Eryk Sun  added the comment:

The Windows API doesn't directly support opening a 'symlink' as Python defines 
it for the follow_symlinks parameter. That problem should be resolved in a 
separate issue. Then updating os.utime() would be relatively trivial.

--
components: +Windows
nosy: +eryksun, paul.moore, steve.dower, tim.golden, zach.ware
stage:  -> needs patch
type:  -> enhancement

___
Python tracker 
<https://bugs.python.org/issue46490>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45382] platform() is not able to detect windows 11

2022-01-24 Thread Eryk Sun


Eryk Sun  added the comment:

> It's *very* unlikely you'll ever get output that doesn't fit into MBCS,

When writing to a pipe, wmic.exe hard codes using the process OEM code page 
(i.e. CP_OEMCP). If it matters, running wmic.exe with subprocess should use 
encoding='oem' instead of text=True.

That said, wmic.exe is deprecated. I suggest using PowerShell instead. For 
example:

import os
import json
import subprocess

cmd = 'Get-CimInstance Win32_OperatingSystem | Select Caption, Version | 
ConvertTo-Json'
p = subprocess.run(f'powershell.exe -c "{cmd}"', capture_output=True, 
encoding=os.device_encoding(1))
result = json.loads(p.stdout)

PowerShell uses the console's output code page (i.e. os.device_encoding(1)) 
when writing to stdout, even if it's a pipe. (If PowerShell is run without a 
console via DETACHED_PROCESS, then it outputs nothing to stdout.) The only way 
I know of to make PowerShell write UTF-8 to stdout when it's a pipe is by 
temporarily changing the console output code page. Assuming the current process 
has a console, you have to first get the current code page with 
GetConsoleOutputCP(). Change the code page to UTF-8 via 
SetConsoleOutputCP(CP_UTF8). Run the PowerShell command. Finally, restore the 
original code page.

Maybe subprocess should provide a context manager to set the console code pages 
before a call, and restore the previous console code pages and console modes 
after a call completes. That's what CLI shells such as CMD do when running an 
external program.

--

___
Python tracker 
<https://bugs.python.org/issue45382>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46506] [Windows] wrap CreateFile to support follow_symlinks

2022-01-24 Thread Eryk Sun


New submission from Eryk Sun :

Issue 46490 proposes to support follow_symlinks in os.utime() on Windows. 
Instead of duplicating the os.stat() implementation of follow_symlinks, I 
suggest factoring out a common _Py_CreateFile() function with two additional 
parameters: traverse (input) and pFileInfo (output).

The `traverse` parameter would indicate whether to traverse or open a 
name-surrogate reparse point. However, FILE_FLAG_OPEN_REPARSE_POINT should take 
precedence over `traverse` and open any type of reparse point.

The pFileInfo parameter would be an optional pointer to a struct that receives 
the file type (disk, char, pipe), file attributes, and reparse tag. Querying 
this information is required when `traverse` is false. There may as well be a 
way to return it.

Since Windows 7 hasn't been supported since 3.8, this is also an opportunity to 
switch to the newer CreateFile2() function [1], which is required in order to 
use new file flags added to the API such as FILE_FLAG_OPEN_REQUIRING_OPLOCK. 

---
[1] 
https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2

--
components: Extension Modules, Windows
messages: 411516
nosy: eryksun, paul.moore, steve.dower, tim.golden, zach.ware
priority: normal
severity: normal
status: open
title: [Windows] wrap CreateFile to support follow_symlinks
type: enhancement

___
Python tracker 
<https://bugs.python.org/issue46506>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46490] Add "follow_symlinks=False" support for "os.utime()" on Windows

2022-01-24 Thread Eryk Sun


Change by Eryk Sun :


--
dependencies: +[Windows] wrap CreateFile to support follow_symlinks

___
Python tracker 
<https://bugs.python.org/issue46490>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45382] platform() is not able to detect windows 11

2022-01-25 Thread Eryk Sun


Eryk Sun  added the comment:

> Bit wmic seems nice solution.
> Is still working for windows lower than 11?

wmic.exe is still included in Windows 10 and 11, but it's officially deprecated 
[1], which means it's no longer being actively developed, and it might be 
removed in a future update. PowerShell is the preferred way to use WMI.

---
[1] 
https://docs.microsoft.com/en-us/windows/deployment/planning/windows-10-deprecated-features

--

___
Python tracker 
<https://bugs.python.org/issue45382>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45382] platform() is not able to detect windows 11

2022-01-25 Thread Eryk Sun


Eryk Sun  added the comment:

> sys.getwindowsversion() which exposes GetVersionEx() looks fine to me.

In Windows 8+, sys.getwindowsversion() returns a version that can vary from 
6.2.9200 (Windows 8) up to the actual OS version. It depends on the OS versions 
supported by the application manifest -- with 6.2.9200 used when there's no 
application manifest. The platform module uses the shell's VER command because 
it returns the real OS version.

The version number for Windows 11 is 10.0.22000+. The platform module 
misreports this as Windows 10. In msg403452, I suggested a modification to use 
build numbers. In msg404451 Steve said we may as well use the WMI result to 
identify Windows 11 based on the OS caption. If we use WMI, we can choose to 
rely on wmic.exe (deprecated), PowerShell, or implement our own code in C/C++.

> I don't understand why we have to handle XML or JSON and encoding... 

Using JSON or XML (ElementTree) isn't required, if you'd rather parse 
human-readable output. AFAIK, the Win32_OperatingSystem caption is always 
ASCII. 

In general, wmic.exe writes output text to a pipe that's encoded with the 
system OEM code page. pwsh.exe, powershell.exe, and cmd.exe write output text 
to a pipe (from their internal commands/cmdlets) that's encoded using the 
console's output code page. cmd.exe also supports a /U option to use UTF-16.

--

___
Python tracker 
<https://bugs.python.org/issue45382>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45382] platform() is not able to detect windows 11

2022-01-26 Thread Eryk Sun

Eryk Sun  added the comment:

> AFAIK, the Win32_OperatingSystem caption is always ASCII. 

I think I was wrong here. The "Caption" field is localized, so the wmic.exe OEM 
encoded output to a pipe isn't reliable. The system OEM code page doesn't 
necessarily match the display/preferred language of the current user. It could 
be a lossy encoding with default and best-fit translations (e.g. "?"; "α" -> 
"a"). If using wmic.exe, it's better to redirect the output to a temp file, 
which will be UTF-16.

--

___
Python tracker 
<https://bugs.python.org/issue45382>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46454] '0 -> /dev/null' is lost

2022-01-26 Thread Eryk Sun

Eryk Sun  added the comment:

> stdin is closed
> os.open() is called and creates a new fd=0
> a call to os.dup2(fd, 0) is made but is a noop

The dup2() silent noop case is a problem, but not the problem that's reported 
in msg43. 

The two examples for this issue are fd.py and fd2.py. In fd.py, "/dev/null" is 
opened, and then, if the fd isn't 0, the fd is duped to inheritable fd 0. In 
fd2.py, fd 0 is closed, and then "/dev/null" is opened as fd 0, which isn't 
inheritable in Python 3.

I think the only problems regarding fd2.py are cross-platform inconsistency 
and/or documentation. If a similar example is tested in Windows it actually 
works, assuming os.devnull is used and that "sleep.exe" is available (e.g. from 
MSYS2). The child's stdin will be the same file as the parent's stdin.

In regard to the standard files, the subprocess documentation states the 
following:

 With the default settings of None, no redirection will occur; 
 the child’s file handles will be inherited from the parent.

 If close_fds is true, all file descriptors except 0, 1 and 2 
 will be closed before the child process is executed.

The above applies only to POSIX. It's not wrong in that case, but it should be 
emphasized that any of the standard file descriptors that's not overridden has 
to be inheritable in POSIX. Also, if overriding to a specific file, the file 
descriptor must be inheritable in POSIX. By default, file descriptors for 
opened files are not inheritable, so the subprocess documentation should 
reference os.set_inheritable(). 

For Windows it says the following:

 if close_fds is true then no handles will be inherited by
 the child process unless explicitly passed in the handle_list
 element of STARTUPINFO.lpAttributeList, or by standard handle 
 redirection.

That's mostly right, except the last part about standard handle redirection is 
vague. If close_fds is true, none of the standard handles of the current 
process is ever inherited in Windows. They could be in principle, but 
subprocess wasn't designed that way. When close_fds is true, and the standard 
handles aren't overridden, subprocess relies on the OS to duplicate (not 
inherit) the standard handles to the child when spawning a console application 
(e.g. "python.exe", but not "pythonw.exe"). If at least one is overridden, 
subprocess duplicates all three as inheritable handles via _make_inheritable(). 
(This is a choice. It doesn't have to do this. It could simply ensure that its 
own pipe/null files are inheritable.) Thus, when close_fds is true it never 
matters whether the current standard handles are inheritable, or whether a 
specified override is inheritable. This behavior is in stark contrast to how 
subprocess works in POSIX.

--

___
Python tracker 
<https://bugs.python.org/issue46454>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46506] [Windows] wrap CreateFile to support follow_symlinks

2022-01-26 Thread Eryk Sun


Eryk Sun  added the comment:

> switch to the newer CreateFile2() function

Apparently we need to allow FILE_SHARE_READ if CreateFile2() is used to 
implement os.stat(). It's not a big deal, but this is a design flaw.

CreateFile2() always uses the kernel I/O flag FILE_DISALLOW_EXCLUSIVE [1] when 
it calls NtCreateFile(). The intent of this flag is to avoid exclusive read 
access on the first open of a file when a user lacks write permission. Thus 
unprivileged users aren't allowed to prevent read access to critical system 
files. 

However, the implementation is flawed because it denies access even if the open 
doesn't request data access. It's also flawed because it allows exclusive read 
access when there are previous opens even if the previous opens have no data 
access.

Classically, IoCheckShareAccess() only checks for a sharing violation when an 
open requests read, write, or delete data access (i.e. FILE_READ_DATA, 
FILE_EXECUTE, FILE_WRITE_DATA, FILE_APPEND_DATA, DELETE). This is clearly 
explained in [MS-FSA] [2]. An open that only requests metadata access can never 
cause a sharing violation and poses no problem with regard to blocking access 
to a file.

---
[1] 
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-iocreatefileex
[2] 
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fsa/8c0e3f4f-0729-49f4-a14d-7f7add593819

--

___
Python tracker 
<https://bugs.python.org/issue46506>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46506] [Windows] wrap CreateFile to support follow_symlinks

2022-01-26 Thread Eryk Sun


Eryk Sun  added the comment:

Here's an implementation of _Py_CreateFile2() and win32_xstat_impl():


typedef struct {
DWORD type;
DWORD attributes;
DWORD reparseTag;
} _PY_CREATE_FILE_INFO;


static HANDLE
_Py_CreateFile2(LPCWSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
DWORD dwCreationDisposition,
LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams,
BOOL traverse,
_PY_CREATE_FILE_INFO *pCreateInfo)
{
HANDLE hFile;
DWORD error;
FILE_BASIC_INFO fbi;
FILE_ATTRIBUTE_TAG_INFO fati;
BOOL openReparsePoint = FALSE;
BOOL traverseFailed = FALSE;
_PY_CREATE_FILE_INFO cfi = {0};

CREATEFILE2_EXTENDED_PARAMETERS createExParams = {
sizeof(CREATEFILE2_EXTENDED_PARAMETERS)};
if (!pCreateExParams) {
pCreateExParams = &createExParams;
}

pCreateExParams->dwFileFlags |= FILE_FLAG_BACKUP_SEMANTICS;

if (pCreateExParams->dwFileFlags & FILE_FLAG_OPEN_REPARSE_POINT) {
openReparsePoint = TRUE;
} else if (!traverse) {
pCreateExParams->dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT;
}

// Share read access if write access isn't requested because
// CreateFile2 uses the NT I/O flag FILE_DISALLOW_EXCLUSIVE.
if (!(dwDesiredAccess & (GENERIC_ALL | GENERIC_WRITE |
FILE_WRITE_DATA | FILE_APPEND_DATA))) {
dwShareMode |= FILE_SHARE_READ;
}

call_createfile:
hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode,
dwCreationDisposition, pCreateExParams);
error = GetLastError(); // success: 0 or ERROR_ALREADY_EXISTS
if (hFile == INVALID_HANDLE_VALUE) {
// bpo-37834: open an unhandled reparse point if traverse fails.
traverseFailed = (error == ERROR_CANT_ACCESS_FILE);
if (openReparsePoint || !(traverse && traverseFailed)) {
return INVALID_HANDLE_VALUE;
}
pCreateExParams->dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT;
hFile = CreateFile2(lpFileName, dwDesiredAccess, dwShareMode,
dwCreationDisposition, pCreateExParams);
if (hFile == INVALID_HANDLE_VALUE) {
SetLastError(error);
return INVALID_HANDLE_VALUE;
}
error = GetLastError(); // 0 or ERROR_ALREADY_EXISTS
}

if (!pCreateInfo && (openReparsePoint || (traverse && !traverseFailed)))
{
return hFile;
}

cfi.type = GetFileType(hFile);
if (cfi.type == FILE_TYPE_UNKNOWN && GetLastError() != 0) {
error = GetLastError();
goto cleanup;
}

if (GetFileInformationByHandleEx(
hFile, FileAttributeTagInfo, &fati, sizeof(fati))) {
cfi.attributes = fati.FileAttributes;
cfi.reparseTag = fati.ReparseTag;
} else if (GetFileInformationByHandleEx(
hFile, FileBasicInfo, &fbi, sizeof(fbi))) {
cfi.attributes = fbi.FileAttributes;
} else {
switch (GetLastError()) {
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_FUNCTION:
case ERROR_NOT_SUPPORTED:
// The file is not in a filesystem.
break;
default:
error = GetLastError();
}
goto cleanup;
}

if (cfi.attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
if (IsReparseTagNameSurrogate(cfi.reparseTag)) {
if (traverseFailed) {
error = ERROR_CANT_ACCESS_FILE;
goto cleanup;
}
} else if (!openReparsePoint && !traverseFailed) {
// Always try to reparse if it's not a name surrogate.
CloseHandle(hFile);
traverse = TRUE;
pCreateExParams->dwFileFlags &= ~FILE_FLAG_OPEN_REPARSE_POINT;
goto call_createfile;
}
}

cleanup:
if (error && error != ERROR_ALREADY_EXISTS) {
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
} else if (pCreateInfo) {
*pCreateInfo = cfi;
}
SetLastError(error);
return hFile;
}


static int
win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
 BOOL traverse)
{
DWORD error;
BY_HANDLE_FILE_INFORMATION fileInfo;
_PY_CREATE_FILE_INFO cfi;
int retval = 0;

HANDLE hFile = _Py_CreateFile2(path, FILE_READ_ATTRIBUTES, 0,
OPEN_EXISTING, NULL, traverse, &cfi);

if (hFile == INVALID_HANDLE_VALUE) {
// Either the path doesn't exist, or the caller lacks access.
error = GetLastError();
switch (error) {
case ERROR_INVALID_PARAMETER:
// The "con" DOS device requires read or write access.
hFile = _Py_CreateFile2(path, GENERIC_READ, FILE_SHARE_READ |
FILE_SHARE_WRITE | F

[issue46550] __slots__ updates despite being read-only

2022-01-27 Thread Eryk Sun


Eryk Sun  added the comment:

Please read about augmented assignment [1]. In the REPL, use help("+=").

An augmented assignment evaluates the target (which, unlike normal
assignment statements, cannot be an unpacking) and the expression
list, performs the binary operation specific to the type of
assignment on the two operands, and assigns the result to the
original target. The target is only evaluated once.

An augmented assignment expression like x += 1 can be rewritten as
x = x + 1 to achieve a similar, but not exactly equal effect. In
the augmented version, x is only evaluated once. Also, when
possible, the actual operation is performed in-place, meaning that
rather than creating a new object and assigning that to the target,
the old object is modified instead.

Unlike normal assignments, augmented assignments evaluate the left-
hand side before evaluating the right-hand side. For example,
a[i] += f(x) first looks-up a[i], then it evaluates f(x) and
performs the addition, and lastly, it writes the result back to
a[i].

---
[1] 
https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46550>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46454] '0 -> /dev/null' is lost

2022-01-27 Thread Eryk Sun


Change by Eryk Sun :


--
Removed message: https://bugs.python.org/msg411776

___
Python tracker 
<https://bugs.python.org/issue46454>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46454] '0 -> /dev/null' is lost

2022-01-27 Thread Eryk Sun


Change by Eryk Sun :


--
Removed message: https://bugs.python.org/msg411287

___
Python tracker 
<https://bugs.python.org/issue46454>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46550] __slots__ updates despite being read-only

2022-01-28 Thread Eryk Sun


Eryk Sun  added the comment:

If the target object of an augmented assignment doesn't support the in-place 
binary operation, the normal binary operation is used instead. Thus an 
augmented assignment is implemented to always assign the result back to the 
target. For an attribute, that's similar to `x.a += 1` -> `x.a = x.a + 1`. For 
example:

>>> dis.dis('x.a += 1')
  0 RESUME   0

  1   2 LOAD_NAME0 (x)
  4 DUP_TOP
  6 LOAD_ATTR1 (a)
  8 LOAD_CONST   0 (1)
 10 BINARY_OP   13 (+=)
 12 ROT_TWO
 14 STORE_ATTR   1 (a)
 16 LOAD_CONST   1 (None)
 18 RETURN_VALUE

Note the STORE_ATTR instruction in the above bytecode.

As to __slots__, I think that class construction should store it as a tuple, 
unless maybe I'm overlooking some use case.

--

___
Python tracker 
<https://bugs.python.org/issue46550>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46572] Unicode identifiers not necessarily unique

2022-01-29 Thread Eryk Sun

Eryk Sun  added the comment:

Please read "Identifiers and keywords" [1] in the documentation. For example:

>>> import unicodedata as ud
>>> ud.normalize('NFKC', '𝖇𝖆𝖗') == 'bar'
True

>>> c = '\N{CYRILLIC SMALL LETTER A}'
>>> ud.name(ud.normalize('NFKC', c))
'CYRILLIC SMALL LETTER A'

---
[1] 
https://docs.python.org/3/reference/lexical_analysis.html?highlight=nfkc#identifiers

--
nosy: +eryksun
resolution:  -> not a bug
stage:  -> resolved
status: open -> closed

___
Python tracker 
<https://bugs.python.org/issue46572>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue29688] Add support for Path.absolute()

2022-01-29 Thread Eryk Sun


Eryk Sun  added the comment:

In Windows, paths that are relative to the current directory on a drive aren't 
resolved. The following should be resolved by the current code:

>>> os.chdir('C:/Temp')
>>> pathlib.Path('C:').absolute()
WindowsPath('C:')

But _from_parts() has bugs with drive-relative paths. 

Assuming the bugs are fixed, when a path has a drive, Path.absolute() should 
resolve against abspath(self.drive) instead of getcwd().

--

___
Python tracker 
<https://bugs.python.org/issue29688>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue44264] Add descriptive error message when environment variable not detected

2022-01-29 Thread Eryk Sun


Eryk Sun  added the comment:

This is just a point of clarification.

> my del did not change the environment variable

os.environ defines the __delitem__ method to call C unsetenv(). Thus `del 
os.environ[varname]` does unset the environment variable, at least at the level 
of the C runtime.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue44264>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46582] Windows builds fail: fatal error RC1116: RC terminating after preprocessor errors

2022-01-30 Thread Eryk Sun


Eryk Sun  added the comment:

PR 29396 and PR 29501 didn't get backported to 3.8 in November. Probably that 
was intentional since 3.8 only gets security fixes since 3.8.10, which was 
released in May. Supporting a newer build environment isn't a security issue. A 
core developer could make an exception for this case, so I'll leave this open 
for now.

--
components: +Windows
versions:  -Python 3.10, Python 3.11, Python 3.9

___
Python tracker 
<https://bugs.python.org/issue46582>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46585] Should we re-export `PyObj_FromPtr` in `ctypes`?

2022-01-30 Thread Eryk Sun


Eryk Sun  added the comment:

Alternatively, one can cast the address to py_object and dereference its 
`value`. For example:

>>> obj = bytearray(b'spam')
>>> sys.getrefcount(obj)
2
>>> obj2 = ctypes.cast(id(obj), ctypes.py_object).value
>>> obj2 is obj
True
>>> sys.getrefcount(obj)
3
>>> del obj2
>>> sys.getrefcount(obj)
2

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46585>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46578] cant DEBUG os.spawnv()

2022-01-30 Thread Eryk Sun


Eryk Sun  added the comment:

> cmd310 = ["/LIBPATH:D:\\python310\\lib\\site-packages\\torch\\lib",

The `argv` parameter of os.spawnv() should begin with a command name to ensure 
that the application parses its command-line correctly. This doesn't 
necessarily have to be a file path since the `path` argument is what gets 
executed. For example, in this case I would use `cmd310 = ["link", ...]`.

That said, for various reasons, including the correct quoting of command-line 
arguments that contain spaces, I recommend that you use the subprocess module 
instead of os.spawnv(). For example, use `p = subprocess.run(cmd310, 
executable=executable)`. The first item of the argument list still needs to be 
"link" in this case. Or include the full path of "link.exe" at the start of 
cmd310, and use `p = subprocess.run(cmd310)`.

> But i cant DEBUG os.spawnv() on Pycharm.

In Windows, os.spawnv() is a builtin function from the nt extension module, 
defined in Modules/posixmodule.c. It is not defined in os.py. The C 
implementation is a minimal wrapper around the C runtime's _wspawnv() function 
[1]. Probably PyCharm isn't capable of debugging a builtin function.

---
[1] 
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/spawnv-wspawnv

--
components: +Windows -Build
nosy: +eryksun, paul.moore, steve.dower, tim.golden, zach.ware

___
Python tracker 
<https://bugs.python.org/issue46578>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46555] Unicode-mangled names refer inconsistently to constants

2022-01-30 Thread Eryk Sun

Eryk Sun  added the comment:

Why was it decided to not raise a syntax error when the NFKC normalization of a 
non-ASCII token matches a keyword? I don't see a use for cases such as `𝕚𝕗 = 1` 
and `𝕚𝕗 + 1`. It seems the cost in terms of confusion far outweighs any 
potential benefit.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46555>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue29688] Add support for Path.absolute()

2022-01-31 Thread Eryk Sun


Eryk Sun  added the comment:

> I'm not seeing what's wrong with your example.

"C:" or "C:spam\\eggs" are not absolute paths. They depend on the effective 
working directory on the drive. An absolute path should never depend on a 
working directory, which can change at random.

WinAPI SetEnvironmentVariableW() allows applications to set environment 
variables with names that begin with "=". These names are effectively reserved 
for special use by the OS, at least as documented. In particular, names of the 
form "=X:", where "X" is a drive letter, are used to store the working 
directory on a drive. The C runtime _[w]chdir() function sets these per-drive 
environment variables, as does Python's os.chdir(). As environment variables, 
they can be inherited by child processes.

When then Windows API resolves a file path to access a file, or in 
GetFullPathNameW(), a drive-relative path such as "X:" or "X:spam\\eggs" is 
resolved against either the current working directory (if it's on the drive) or 
the value of the "=X:" environment variable for the drive. If the latter isn't 
defined, it defaults to the root directory, e.g. "X:\\". If the current working 
directory is on the drive, the system updates the value of the "=X:" 
environment variable, if it exists.

> on Windows you have to resolve the drive separately from the 
> working directory and then concatenate them?

Yes, if self.drive is defined. This would be handled by 
os.path.abspath(self.drive), which calls WinAPI GetFullPathNameW().

--

___
Python tracker 
<https://bugs.python.org/issue29688>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue29688] Add support for Path.absolute()

2022-01-31 Thread Eryk Sun


Change by Eryk Sun :


--
Removed message: https://bugs.python.org/msg412220

___
Python tracker 
<https://bugs.python.org/issue29688>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue29688] Add support for Path.absolute()

2022-01-31 Thread Eryk Sun


Eryk Sun  added the comment:

> I'm not seeing what's wrong with your example.

"C:" or "C:spam\\eggs" are not absolute paths. They depend on the effective 
working directory on the drive. An absolute path should never depend on a 
working directory, which can change at random.

WinAPI SetEnvironmentVariableW() allows applications to set environment 
variables with names that begin with "=". These names are effectively reserved 
for special use by the OS, at least as documented. In particular, names of the 
form "=X:", where "X" is a drive letter, are used to store the working 
directory on a drive. The C runtime _[w]chdir() function sets these per-drive 
environment variables, as does Python's os.chdir(). As environment variables, 
they can be inherited by child processes.

When then Windows API resolves a file path to access a file, or in 
GetFullPathNameW(), a drive-relative path such as "X:" or "X:spam\\eggs" is 
resolved against either the current working directory (if it's on the drive) or 
the value of the "=X:" environment variable for the drive. If the latter isn't 
defined, it defaults to the root directory, e.g. "X:\\". If the current working 
directory is on the drive, the system updates the value of the "=X:" 
environment variable, if it exists.

> on Windows you have to resolve the drive separately from the 
> working directory and then concatenate them?

No, if self.drive is defined, then abspath(self.drive) should be called instead 
of getcwd(). In Windows, ntpath.abspath() calls WinAPI GetFullPathNameW(), 
which resolves the working directory on the drive.

--

___
Python tracker 
<https://bugs.python.org/issue29688>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue29688] Add support for Path.absolute()

2022-01-31 Thread Eryk Sun


Eryk Sun  added the comment:

> I'd imagine that bug is reproducible with `Path('C:\\Temp', 'C:')` 
> already, right? If that's the case, should it logged as a 
> separate issue?

Yes, it's a separate issue that affects the _from_parts() call in absolute().

How about designing absolute() to create a new instance from an absolute path 
that's created by os.path? For example: 

join(abspath(self.drive) if self.drive else getcwd(), self)

Of course use accessor functions.

--

___
Python tracker 
<https://bugs.python.org/issue29688>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46594] Windows "Edit with IDLE >" only has one selection

2022-01-31 Thread Eryk Sun


Eryk Sun  added the comment:

Check your settings in the registry.

In "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts" there 
should be a ".py" key, but not necessarily. It should have an "OpenWithList" 
subkey that contains an "MRUList" value (most recently used list). The value 
should be a sequence of letters, each of which should be a value name in the 
key. If the launcher was last used to open a ".py" file, the first letter in 
the list should be a value with the data "py.exe".

Ideally there should also be a subkey named "UserChoice" that contains a 
"ProgId" (programmatic identifier) value with the data "Python.File". This sets 
the "Python.File" ProgID as the locked-in user choice for ".py" files. In the 
GUI, you can set this in the open-with dialog by selecting "always use this app 
to open .py files". The selected app should be "Python", with an icon that 
contains the Python logo and a rocket (the launcher).

If ".py" isn't the locked-in user choice, the shell API will use the most 
recent user selection in the open-with menu. If there's no user selection, the 
default association is calculated from "HKCR\.py", which is a merged view of 
"[HKCU|HKLM]\Software\Classes\.py". The default value of "HKCR\.py" sets the 
default file association. Ideally it should be "Python.File".

"HKCR\Python.File" is a merged view of 
"[HKCU|HKLM]\Software\Classes\Python.File". For the merged view, if a value 
name is defined in the same subkey of HKCU and HKLM, the view prefers the HKCU 
value. There should be a subkey named "shell\editwithidle\shell". It should 
define one or more subkeys named "edit3*", such as "edit310". Each should 
contain a "MUIVerb" value that sets the command description in the "open-with" 
menu. There should also be a "command" subkey that contains the template 
command as its default value, e.g. ""C:\Program Files\Python310\pythonw.exe" -m 
idlelib "%L" %*".

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46594>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46594] Windows "Edit with IDLE >" only has one selection

2022-02-01 Thread Eryk Sun


Eryk Sun  added the comment:

> the Open With entries may not be being merged.

That would probably be a bug in the Windows shell API. The HKCU and HKLM 
subkeys of "Software\Classes\Python.File\Shell\editwithidle\shell" are merged 
in the HKCR view. The same key path can exist in both hives, for which the view 
contains the union, with precedence for HKCU.

For example, with "Software\Classes\spam":

import winreg

hkm = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, r'Software\Classes\spam')
winreg.SetValueEx(hkm, 'eggs', 0, winreg.REG_SZ, 'hklm')
winreg.SetValueEx(hkm, 'baz', 0, winreg.REG_SZ, 'hklm')

hku = winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\Classes\spam')
winreg.SetValueEx(hku, 'eggs', 0, winreg.REG_SZ, 'hkcu')
winreg.SetValueEx(hku, 'bam', 0, winreg.REG_SZ, 'hkcu')

hkr = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'spam')

>>> winreg.EnumValue(hkr, 0)
('bam', 'hkcu', 1)
>>> winreg.EnumValue(hkr, 1)
('baz', 'hklm', 1)
>>> winreg.EnumValue(hkr, 2)
('eggs', 'hkcu', 1)

>>> winreg.EnumValue(hkr, 3)
Traceback (most recent call last):
  File "", line 1, in 
OSError: [WinError 259] No more data is available

--

___
Python tracker 
<https://bugs.python.org/issue46594>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46490] Add "follow_symlinks=False" support for "os.utime()" on Windows

2022-02-02 Thread Eryk Sun


Eryk Sun  added the comment:

In case you missed it, I implemented _Py_CreateFile2() in bpo-46506 and rewrote 
os.stat() based on it. Check it out in case you're interested in moving forward 
with a PR in bpo-46506.

For this issue, follow_symlinks is fairly simple to support with 
_Py_CreateFile2(). We may as well add fd support, since that's trivial to add. 
For example:

if (path->fd != -1) {
hFile = _Py_get_osfhandle(path->fd);
} else {
Py_BEGIN_ALLOW_THREADS
hFile = _Py_CreateFile2(path->wide, FILE_WRITE_ATTRIBUTES, 0,
OPEN_EXISTING, NULL, follow_symlinks, NULL);
Py_END_ALLOW_THREADS
}
if (hFile == INVALID_HANDLE_VALUE) {
if (path->fd == -1) {
path_error(path);
}
return NULL;
}

One also has to define the following macros to declare follow_symlinks and fd 
support: UTIME_HAVE_NOFOLLOW_SYMLINKS and PATH_UTIME_HAVE_FD.

To announce support in os.supports_follow_symlinks and os.supports_fd, it 
should be conditioned on MS_WINDOWS, i.e. _add("MS_WINDOWS", "utime"). The os 
module is frozen, so changing these two sets requires rebuilding Python.

--

___
Python tracker 
<https://bugs.python.org/issue46490>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46631] Implement a "strict" mode for getpass.getuser()

2022-02-03 Thread Eryk Sun


New submission from Eryk Sun :

getpass.getuser() checks the environment variables LOGNAME (login name), USER, 
LNAME, and USERNAME, in that order. In Windows, LOGNAME, USER, and LNAME have 
no conventional usage. I think there should be a strict mode that restricts 
getuser() to check only USERNAME in Windows and only LOGNAME in POSIX [1]. If 
the login variable isn't defined, it should fall back on using the system API, 
based on the user ID in POSIX and the logon ID in Windows.

For the fallback in Windows, the _winapi module could implement 
GetCurrentProcessToken(), GetTokenInformation(), and LsaGetLogonSessionData(). 
For TokenStatistics, return a dict with just "AuthenticationId". For 
LsaGetLogonSessionData(), return a dict with just "UserName". 
GetCurrentProcessToken() returns a pseudohandle (-4), which should not be 
closed.

For example, assuming _winapi wraps the required functions:

def getuser(strict=False):
"""Get the username from the environment or password database.

First try various environment variables. If strict, check only LOGNAME
in POSIX and only USERNAME in Windows. As a fallback, in POSIX get the
user name from the password database, and in Windows get the user name
from the logon-session data of the current process.
"""
posix = sys.platform != 'win32'

if strict:
names = ('LOGNAME',) if posix else ('USERNAME',)
else:
names = ('LOGNAME', 'USER', 'LNAME', 'USERNAME')

for name in names:
if user := os.environ.get(name):
return user

if posix:
import pwd
return pwd.getpwuid(os.getuid())[0]

import _winapi
logon_id = _winapi.GetTokenInformation(
_winapi.GetCurrentProcessToken(),
_winapi.TokenStatistics)['AuthenticationId']
return _winapi.LsaGetLogonSessionData(logon_id)['UserName']

Like WinAPI GetUserNameW(), the above fallback returns the logon user name 
instead of the account name of the token user. As far as I know, the user name 
and the account name only differ for the builtin service account logons 
"SYSTEM" (999) and "NETWORK SERVICE" (996), for which the user name is the 
machine security principal (i.e. the machine's NETBIOS name plus "$"). The user 
name of the builtin "LOCAL SERVICE" logon (997), on the other hand, is just the 
"LOCAL SERVICE" account name, since this account lacks network access.

Unlike GetUserNameW(), the above code uses the process token instead of the 
effective token. This is like POSIX getuid(), whereas what GetUserNameW() does 
is like geteuid(). getuser() could implement an `effective` option to return 
the effective user name. In Windows this would switch to calling 
GetCurrentThreadEffectiveToken() instead of GetCurrentProcessToken().

---

[1] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html

--
components: Library (Lib), Windows
messages: 412495
nosy: eryksun, paul.moore, steve.dower, tim.golden, zach.ware
priority: normal
severity: normal
stage: needs patch
status: open
title: Implement a "strict" mode for getpass.getuser()
type: enhancement
versions: Python 3.11

___
Python tracker 
<https://bugs.python.org/issue46631>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46631] Implement a "strict" mode for getpass.getuser()

2022-02-04 Thread Eryk Sun


Eryk Sun  added the comment:

Here's an example for the suggested changes to _winapi.

Include these headers:

#include  // LsaGetLogonSessionData
#include  // STATUS_SUCCESS

Add these argument-clinic macros to _winapi_functions:

_WINAPI_GETCURRENTPROCESSTOKEN_METHODDEF
_WINAPI_GETTOKENINFORMATION_METHODDEF
_WINAPI_LSAGETLOGONSESSIONDATA_METHODDEF

Add TokenStatistics in winapi_exec():

WINAPI_CONSTANT(F_DWORD, TokenStatistics);

Add minimal implementations that wrap the WinAPI functions:

/*[clinic input]
_winapi.GetCurrentProcessToken -> HANDLE

Return a handle for the access token of the current process.
[clinic start generated code]*/

static HANDLE
_winapi_GetCurrentProcessToken_impl(PyObject *module)
/*[clinic end generated code: output=cf8e8e20dd41dd6e 
input=73a282cf3718af9e]*/

{
return GetCurrentProcessToken();
}


/*[clinic input]
_winapi.GetTokenInformation

handle: HANDLE
information_class: unsigned_long
/

Get information from an access token.
[clinic start generated code]*/

static PyObject *
_winapi_GetTokenInformation_impl(PyObject *module, HANDLE handle,
 unsigned long information_class)
/*[clinic end generated code: output=caecec0a25658348 
input=b277ad2414f1b03e]*/

{
if (information_class != TokenStatistics) {
return PyErr_Format(
PyExc_NotImplementedError,
"Unsupported information class: %d",
information_class);
}

DWORD returned_size;
TOKEN_STATISTICS info;

if (!GetTokenInformation(handle, information_class, &info, sizeof(info),
&returned_size)) {
return PyErr_SetFromWindowsErr(0);
}

PyObject *result = PyDict_New();
if (!result) {
return NULL;
}

PyObject *value = PyLong_FromUnsignedLongLong(
(((uint64_t)info.AuthenticationId.HighPart) << 32) +
info.AuthenticationId.LowPart);
if (!value) {
goto error;
}
if (PyDict_SetItemString(result, "AuthenticationId", value) < 0) {
Py_DECREF(value);
goto error;
}
Py_DECREF(value);

return result;

error:
Py_CLEAR(result);
return NULL;
}


/*[clinic input]
_winapi.LsaGetLogonSessionData

logon_id: unsigned_long_long
/
Get data for the logon session identified by logon_id.
[clinic start generated code]*/

static PyObject *
_winapi_LsaGetLogonSessionData_impl(PyObject *module,
unsigned long long logon_id)
/*[clinic end generated code: output=680ac7725ef34527 
input=01ff4216b89d01ef]*/

{
SECURITY_LOGON_SESSION_DATA *pdata;
LUID logon_luid;
logon_luid.HighPart = logon_id >> 32;
logon_luid.LowPart = logon_id & 0x;

NTSTATUS status = LsaGetLogonSessionData(&logon_luid, &pdata);
if (status != STATUS_SUCCESS) {
return PyErr_SetFromWindowsErr(LsaNtStatusToWinError(status));
}

PyObject *result = PyDict_New();
if (!result) {
goto error;
}

PyObject *value = PyUnicode_FromWideChar(pdata->UserName.Buffer,
pdata->UserName.Length / sizeof(WCHAR));
if (!value) {
goto error;
}
if (PyDict_SetItemString(result, "UserName", value) < 0) {
Py_DECREF(value);
goto error;
}
Py_DECREF(value);

LsaFreeReturnBuffer(pdata);
return result;

error:
LsaFreeReturnBuffer(pdata);
Py_CLEAR(result);
return NULL;
}

--

___
Python tracker 
<https://bugs.python.org/issue46631>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46654] file_open doesn't handle UNC paths produced by pathlib's resolve() (but can handle UNC paths with additional slashes)

2022-02-05 Thread Eryk Sun


Eryk Sun  added the comment:

Builtin open() calls C open(). This C function supports whatever path types are 
supported natively. In Windows, C open() calls WinAPI CreateFileW(), which does 
not support "file://" URIs. The Windows API handles it as a relative path, 
which gets resolved against the current working directory. For example:

>>> os.getcwd()
'C:\\Temp'
>>> nt._getfullpathname('file:host/share/file')
'C:\\Temp\\file:\\host\\share\\file'
>>> nt._getfullpathname('file://host/share/file')
'C:\\Temp\\file:\\host\\share\\file'

As to the resolved path somehow working, that generally will not be the case. 
Most filesystems in Windows will reject a path component named "file:" as an 
invalid name. The ":" character is usually disallowed in base file and 
directory names, since some Windows filesystems use it as a delimiter in file 
streams, e.g. "name:stream_name:stream_type". The default data stream in a 
regular file has no stream name, and its stream type is "$DATA". Thus for base 
name "file", the default data stream can be referenced explicitly as 
"file::$DATA". But just "file:", with neither a stream name nor a stream type, 
is an invalid name.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46654>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46654] file_open doesn't handle UNC paths produced by pathlib's resolve() (but can handle UNC paths with additional slashes)

2022-02-05 Thread Eryk Sun


Change by Eryk Sun :


--
Removed message: https://bugs.python.org/msg412604

___
Python tracker 
<https://bugs.python.org/issue46654>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46654] urllib.request.urlopen doesn't handle UNC paths produced by pathlib's resolve() (but can handle UNC paths with additional slashes)

2022-02-05 Thread Eryk Sun


Eryk Sun  added the comment:

In FileHandler.file_open(), req.host is the host name, which is either None or 
an empty string for a local drive path such as, respectively, 
"file:/Z:/test.py" or "file:///Z:/test.py". The value of req.selector never 
starts with "//", for which file_open() checks, but rather a single slash, such 
as "/Z:/test.py" or "/share/test.py". This is a bug in file_open(). Due to this 
bug, it always calls self.open_local_file(req), even if req.host isn't local. 
The distinction shouldn't matter in Windows, which supports UNC paths, but 
POSIX has to open a path on the local machine (possibly a mount point for a 
remote path, but that's irrelevant). In POSIX, if the local machine 
coincidentally has the req.selector path, then the os.stat() and open() calls 
will succeed with a bogus result.

For "file://host/share/test.py", req.selector is "/share/test.py". In Windows, 
url2pathname() converts this to r"\share\test.py", which is relative to the 
drive of the process current working directory. This is a bug in 
open_local_file() on Windows. For it to work correctly, req.host has to be 
joined back with req.selector as the UNC path "//host/share/test.py". Of 
course, this need not be a local file in Windows, so Windows should be exempted 
from the local file limitation in file_open().

--

___
Python tracker 
<https://bugs.python.org/issue46654>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46654] urllib.request.urlopen doesn't handle UNC paths produced by pathlib's resolve() (but can handle UNC paths with additional slashes)

2022-02-05 Thread Eryk Sun


Eryk Sun  added the comment:

> file://server/host/file.ext on windows, even though 
> file:server/host/file.ext open just fine.

For r"\\host\share\test.py", the two slash conversion 
"file://host/share/test.py" is correct according to RFC80889 "E.3.1.  URI 
with Authority" [1]. In this case, req.host is "host", and req.selector is 
"/share/test.py". 

The four slash version "file:host/share/test.py" is a known variant for a 
converted UNC path, as noted in RFC8089 "E.3.2.  URI with UNC Path". In 
this case, req.host is an empty string, and req.selector is 
"//host/share/test.py". There's another variant that uses 5 slashes for a UNC 
path, but urllib (or url2pathname) doesn't support it.

---
[1] https://datatracker.ietf.org/doc/html/rfc8089

--

___
Python tracker 
<https://bugs.python.org/issue46654>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46654] urllib.request.urlopen doesn't handle UNC paths produced by pathlib's as_uri() (but can handle UNC paths with additional slashes)

2022-02-06 Thread Eryk Sun


Eryk Sun  added the comment:

> The value of req.selector never starts with "//", for which file_open() 
> checks, but rather a single slash, such as "/Z:/test.py" or 
> "/share/test.py".

To correct myself, actually req.selector will start with "//" for a "file:" 
URI, such as "file:host/share/test.py". For this example, req.host is an 
empty string, so file_open() still ends up calling open_local_file(), which 
will open "//host/share/test.py". In Linux, "//host/share" is the same as 
"/host/share". In Cygwin and MSYS2 it's a UNC path. I guess this case should be 
allowed, even though the meaning of a "//" root isn't specifically defined in 
POSIX.

Unless I'm overlooking something, file_open() only has to check the value of 
req.host. In POSIX, it should require opening a 'local' path, i.e. if req.host 
isn't None, empty, or a local host, raise URLError.

In Windows, my tests show that the shell API special cases "localhost" (case 
insensitive) in "file:" URIs. For example, the following are all equivalent: 
"file:/C:/Temp", "file:///C:/Temp", and "file://localhost/C:/Temp". The shell 
API does not special case the real local host name or any of its IP addresses, 
such as 127.0.0.1. They're all handled as UNC paths.

Here's what I've experimented with thus far, which passes the existing urllib 
tests in Linux and Windows:

class FileHandler(BaseHandler):
def file_open(self, req):
if not self._is_local_path(req):
if sys.platform == 'win32':
path = url2pathname(f'//{req.host}{req.selector}')
else:
raise URLError("In POSIX, the file:// scheme is only "
   "supported for local file paths.")
else:
path = url2pathname(req.selector)
return self._common_open_file(req, path)


def _is_local_path(self, req):
if req.host:
host, port = _splitport(req.host)
if port:
raise URLError(f"the host cannot have a port: {req.host}")
if host.lower() != 'localhost':
# In Windows, all other host names are UNC.
if sys.platform == 'win32':
return False
# In POSIX, support all names for the local host.
if _safe_gethostbyname(host) not in self.get_names():
return False
return True


# names for the localhost
names = None
def get_names(self):
if FileHandler.names is None:
try:
FileHandler.names = tuple(
socket.gethostbyname_ex('localhost')[2] +
socket.gethostbyname_ex(socket.gethostname())[2])
except socket.gaierror:
FileHandler.names = (socket.gethostbyname('localhost'),)
return FileHandler.names


def open_local_file(self, req):
if not self._is_local_path(req):
raise URLError('file not on local host')
return self._common_open_file(req, url2pathname(req.selector))


def _common_open_file(self, req, path):
import email.utils
import mimetypes
host = req.host
filename = req.selector
try:
if host:
origurl = f'file://{host}{filename}'
else:
origurl = f'file://{filename}'
stats = os.stat(path)
size = stats.st_size
modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
mtype = mimetypes.guess_type(filename)[0] or 'text/plain'
headers = email.message_from_string(
f'Content-type: {mtype}\n'
f'Content-length: {size}\n'
f'Last-modified: {modified}\n')
return addinfourl(open(path, 'rb'), headers, origurl)
except OSError as exp:
raise URLError(exp)


Unfortunately nturl2path.url2pathname() parses some UNC paths incorrectly. For 
example, the following path should be an invalid UNC path, since "C:" is an 
invalid name, but instead it gets converted into an unrelated local path.

>>> nturl2path.url2pathname('//host/C:/Temp/spam.txt')
'C:\\Temp\\spam.txt'

This goof depends on finding ":" or "|" in the path. It's arguably worse if the 
last co

[issue46654] urllib.request.urlopen doesn't handle UNC paths produced by pathlib's as_uri() (but can handle UNC paths with additional slashes)

2022-02-06 Thread Eryk Sun


Change by Eryk Sun :


--
assignee: docs@python -> 
components:  -2to3 (2.x to 3.x conversion tool), Argument Clinic, Build, C API, 
Cross-Build, Demos and Tools, Distutils, Documentation, Extension Modules, 
FreeBSD, IDLE, IO, Installation, Interpreter Core, Parser, Regular Expressions, 
SSL, Subinterpreters, Tests, Tkinter, Unicode, Windows, XML, asyncio, ctypes, 
email, macOS
stage:  -> needs patch
type: performance -> behavior
versions:  -Python 3.7, Python 3.8

___
Python tracker 
<https://bugs.python.org/issue46654>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46668] encodings: the "mbcs" alias doesn't work

2022-02-06 Thread Eryk Sun


Eryk Sun  added the comment:

> The Python 3.6 and 3.7 "codecs.register(_alias_mbcs)" doesn't work 
> because "search_function()" is tested before and it works for "cpXXX" 
> encodings.

Isn't the 3.6-3.10 ordering of search_function() and _alias_mbcs() correct as a 
fallback? In this case, Python doesn't support a cross-platform encoding for 
the code page. That's why the old implementation of test_mbcs_alias() mocked 
_winapi.GetACP() to return 123 and then checked that looking up 'cp123' 
returned the "mbcs" codec.

I'd actually prefer to extend this by implementing _winapi.GetOEMCP() and using 
"oem" as a fallback for that case.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46668>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46668] encodings: the "mbcs" alias doesn't work

2022-02-07 Thread Eryk Sun


Eryk Sun  added the comment:

> I don't think that this fallback is needed anymore. Which Windows
> code page can be used as ANSI code page which is not already 
> implemented as a Python codec?

Python has full coverage of the ANSI and OEM code pages in the standard Windows 
locales, but I don't have any experience with custom (i.e. supplemental or 
replacement) locales.

https://docs.microsoft.com/en-us/windows/win32/intl/custom-locales 

Here's a simple script to check the standard locales.

import codecs
import ctypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

LOCALE_ALL = 0
LOCALE_WINDOWS = 1
LOCALE_IDEFAULTANSICODEPAGE = 0x1004
LOCALE_IDEFAULTCODEPAGE = 0x000B # OEM

EnumSystemLocalesEx = kernel32.EnumSystemLocalesEx
GetLocaleInfoEx = kernel32.GetLocaleInfoEx
GetCPInfoExW = kernel32.GetCPInfoExW

EnumLocalesProcEx = ctypes.WINFUNCTYPE(ctypes.c_int,
ctypes.c_wchar_p, ctypes.c_ulong, ctypes.c_void_p)

class CPINFOEXW(ctypes.Structure):
 _fields_ = (('MaxCharSize', ctypes.c_uint),
 ('DefaultChar', ctypes.c_ubyte * 2),
 ('LeadByte', ctypes.c_ubyte * 12),
 ('UnicodeDefaultChar', ctypes.c_wchar),
 ('CodePage', ctypes.c_uint),
 ('CodePageName', ctypes.c_wchar * 260))

def get_all_locale_code_pages():
result = []
seen = set()
info = (ctypes.c_wchar * 100)()

@EnumLocalesProcEx
def callback(locale, flags, param):
for lctype in (LOCALE_IDEFAULTANSICODEPAGE, 
LOCALE_IDEFAULTCODEPAGE):
if (GetLocaleInfoEx(locale, lctype, info, len(info)) and
  info.value not in ('0', '1')):
cp = int(info.value)
if cp in seen:
continue
seen.add(cp)
cp_info = CPINFOEXW()
if not GetCPInfoExW(cp, 0, ctypes.byref(cp_info)):
cp_info.CodePage = cp
cp_info.CodePageName = str(cp)
result.append(cp_info)
return True

if not EnumSystemLocalesEx(callback, LOCALE_WINDOWS, None, None):
raise ctypes.WinError(ctypes.get_last_error())

result.sort(key=lambda x: x.CodePage)
return result

supported = []
unsupported = []
for cp_info in get_all_locale_code_pages():
cp = cp_info.CodePage
try:
codecs.lookup(f'cp{cp}')
except LookupError:
unsupported.append(cp_info)
else:
supported.append(cp_info)

if unsupported:
print('Unsupported:\n')
for cp_info in unsupported:
print(cp_info.CodePageName)
print('\nSupported:\n')
else:
print('All Supported:\n')
for cp_info in supported:
print(cp_info.CodePageName)


Output:

All Supported:

437   (OEM - United States)
720   (Arabic - Transparent ASMO)
737   (OEM - Greek 437G)
775   (OEM - Baltic)
850   (OEM - Multilingual Latin I)
852   (OEM - Latin II)
855   (OEM - Cyrillic)
857   (OEM - Turkish)
862   (OEM - Hebrew)
866   (OEM - Russian)
874   (ANSI/OEM - Thai)
932   (ANSI/OEM - Japanese Shift-JIS)
936   (ANSI/OEM - Simplified Chinese GBK)
949   (ANSI/OEM - Korean)
950   (ANSI/OEM - Traditional Chinese Big5)
1250  (ANSI - Central Europe)
1251  (ANSI - Cyrillic)
1252  (ANSI - Latin I)
1253  (ANSI - Greek)
1254  (ANSI - Turkish)
1255  (ANSI - Hebrew)
1256  (ANSI - Arabic)
1257  (ANSI - Baltic)
1258  (ANSI/OEM - Viet Nam)

Some locales are Unicode only (e.g. Hindi-India) or have no OEM code page, 
which the above code skips by checking for "0" or "1" as the code page value. 
Windows 10+ allows setting the system locale to a Unicode-only locale, for 
which it uses UTF-8 (65001) for ANSI and OEM.

The OEM code page matters because the console input and output code pages 
default to OEM, e.g. for os.device_encoding(). The console's I/O code pages are 
used in Python by low-level os.read() and os.write(). Note that the console 
doesn't properly implement using UTF-8 (65001) as the input code page. In this 
case, input read from the console via ReadFile() or ReadConsoleA() has a null 
byte in place of each non-ASCII character.

--

___
Python tracker 
<https://bugs.python.org/issue46668>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46659] Deprecate locale.getdefaultlocale() function

2022-02-08 Thread Eryk Sun

Eryk Sun  added the comment:

> getdefaultlocale() falls back to LANG and LANGUAGE.

_Py_SetLocaleFromEnv(LC_CTYPE) (e.g. setlocale(LC_CTYPE, "")) gets called at 
startup, except for the isolated configuration [1].

I think calendar.Locale*Calendar should try the LC_CTYPE locale if LC_TIME is 
"C", i.e. (None, None). Otherwise, it's introducing new default behavior. For 
example, with LC_ALL set to "ru_RU.utf8":

3.8:

>>> locale.getlocale(locale.LC_TIME)
(None, None)
>>> locale.getlocale(locale.LC_CTYPE)
('ru_RU', 'UTF-8')
>>> cal = calendar.LocaleTextCalendar()
>>> cal.formatweekday(0, 15)
'  Понедельник  '

3.11.0a5+:

>>> locale.getlocale(locale.LC_TIME)
(None, None)
>>> locale.getlocale(locale.LC_CTYPE)
('ru_RU', 'UTF-8')
>>> cal = calendar.LocaleTextCalendar()
>>> cal.formatweekday(0, 15)
' Monday'
>>> locale.setlocale(locale.LC_TIME, '')
'ru_RU.utf8'
>>> cal = calendar.LocaleTextCalendar()
>>> cal.formatweekday(0, 15)
'  Понедельник  '

---

[1] https://docs.python.org/3/c-api/init_config.html?#isolated-configuration

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46659>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46659] Deprecate locale.getdefaultlocale() function

2022-02-08 Thread Eryk Sun


Eryk Sun  added the comment:

> Oh. Serhiy asked me to use LC_TIME rather than LC_CTYPE.

Since Locale*Calendar is documented as not being thread safe, __init__() could 
get the real default via setlocale(LC_TIME, "") when locale=None and the 
current LC_TIME is "C". Restore it back to "C" after getting the default. That 
should usually match the behavior from previous versions that called 
getdefaultlocale(). In cases where it differs, it's fixing a bug because the 
default LC_TIME is the correct default.

--

___
Python tracker 
<https://bugs.python.org/issue46659>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46686] [venv / PC/launcher] issue with a space in the installed python path

2022-02-08 Thread Eryk Sun


Eryk Sun  added the comment:

run_child() expects `cmdline` to be correctly quoted, and normally it is.

I can't reproduce this problem with Python 3.10.2. I created a user account 
with a space in the account name, logged on, and installed 3.10.2 for the 
current user, with the option enabled to add Python to PATH. Next I opened a 
command prompt in the user profile directory and created a virtual environment 
via `python.exe -m venv .venv`. Running ".venv\Scripts\python.exe -X utf8" 
worked. The command line used by the venv "python.exe" launcher was properly 
quoted and thus properly parsed in the original list of command-line arguments, 
sys.orig_argv.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46686>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46686] [venv / PC/launcher] issue with a space in the installed python path

2022-02-08 Thread Eryk Sun


Eryk Sun  added the comment:

I checked the source code in PC/launcher.c process(). It turns out that 
`executable` is not getting quoted in the venv launcher case.

CreateProcessW() tries to get around this. If the command isn't quoted, it has 
a loop that consumes up to a space (or tab) and checks for an existing file 
(not a directory). If it finds a file, it rewrites the command line to quote 
the path of the file.

My test happened to work. But it's simple enough to create an example that 
fails. For example, as an elevated admin, create a file named "C:\Program". Now 
the venv launcher won't be able to execute a base interpreter that's installed 
in "C:\Program Files":

C:\Temp>echo >C:\Program

C:\Temp>"C:\Program Files\Python310\python.exe" -m venv env
Error: Command '['C:\\Temp\\env\\Scripts\\python.exe', '-Im', 
'ensurepip', '--upgrade', '--default-pip']' returned non-zero 
exit status 101.

--
priority: normal -> critical
stage:  -> needs patch
versions: +Python 3.11

___
Python tracker 
<https://bugs.python.org/issue46686>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46686] [venv / PC/launcher] issue with a space in the installed python path

2022-02-08 Thread Eryk Sun


Eryk Sun  added the comment:

The venv launcher can quote the executable path either always or only when it 
contains spaces. The following is a suggestion for implementing the latter.

Before allocating `executable`, use memchr() (include ) to search the 
UTF-8 source for a space. If found, increment the character count by two. After 
allocating `executable`, add the initial quote character, and increment the 
pointer.

BOOL add_quotes = FALSE;
if (memchr(start, ' ', (size_t)len) != NULL) {
add_quotes = TRUE;
cch += 2;
}

executable = (wchar_t *)malloc(cch * sizeof(wchar_t));
if (executable == NULL) {
error(RC_NO_MEMORY, L"A memory allocation failed");
}

if (add_quotes) {
*executable++ = L'\"';
}

Later, after checking the existence via GetFileAttributesW(), add the trailing 
quote and null, and decrement the pointer:

if (GetFileAttributesW(executable) == INVALID_FILE_ATTRIBUTES) {
error(RC_NO_PYTHON, L"No Python at '%ls'", executable);
}

if (add_quotes) {
size_t n = wcslen(executable);
executable[n] = L'\"';
executable[n + 1] = L'\0';
executable--;
}

--

___
Python tracker 
<https://bugs.python.org/issue46686>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46697] _ctypes_simple_instance returns inverted logic

2022-02-09 Thread Eryk Sun


Change by Eryk Sun :


--
stage:  -> patch review
versions:  -Python 3.7, Python 3.8

___
Python tracker 
<https://bugs.python.org/issue46697>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46703] boolean operation issue (True == False == False)

2022-02-09 Thread Eryk Sun


Eryk Sun  added the comment:

> True == False == False is really True == False and False == False 
> wich is False and True which is False

Moreover, since the left-hand comparison is `True == False`, which evaluates to 
False, the right-hand comparison doesn't even get evaluated. 

In the following example, print(3) doesn't get called because the left-hand 
comparison, `None != None`, is False:

>>> print(1) != print(2) == print(3)
1
2
False

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46703>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46716] regrtest didn't respect the timeout when running test_subprocess on AMD64 Windows11 3.x

2022-02-11 Thread Eryk Sun


Eryk Sun  added the comment:

> test_call_timeout() or test_timeout() in test_subprocess.py.

These tests don't override the standard files, and they only spawn a single 
child with no descendants. I don't see why this would hang. It shouldn't be a 
problem with leaked pipe handles (see bpo-43346). It probably will need to be 
diagnosed by attaching a debugger, or offline with a dump file.

> process trees whereas terminating a parent automatically kills the children

One can use a job object to manage a child process and all of its descendants, 
including resource usage and termination. A process can belong to multiple job 
objects in Windows 8+, which is required by Python 3.9+.

For reliability, the child has to be created in a suspended state via 
CREATE_SUSPENDED. It can be resumed with ResumeThread() after adding it to the 
job with AssignProcessToJobObject().

You can try to terminate a job cleanly, which is similar in effect to sending 
SIGTERM to a process group in POSIX. In Windows, this has to be approached 
differently for console vs graphical processes.

To handle console apps, assuming the child inherits the current console, spawn 
it as a new process group via 
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP. You can request an exit by 
sending a Ctrl+Break event to the group via os.kill(p.pid, 
signal.CTRL_BREAK_EVENT) [1]. The request might be ignored, but typically the 
default handler is called, which calls ExitProcess().

To handle GUI apps, assuming the child inherits the current desktop (usually 
"WinSta0\Default"), first enumerate the top-level and message-only windows on 
the current desktop via EnumWindows() and FindWindowExW(). Use 
GetWindowThreadProcessId() to filter the list to include only windows that 
belong to the job. Post WM_CLOSE to each window in the job. A process might 
ignore a request to close. It could keep the window open or continue running in 
the background.

After an internal timeout, you can call TerminateJobObject() to kill any 
process in the job that remains alive. This is a forced and abrupt termination, 
which is similar to sending SIGKILL to a process group in POSIX.

---

[1] This usage of os.kill() is what we're stuck with. Rightfully, we should be 
using os.killpg(p.pid, signal.SIGBREAK) or os.kill(-p.pid, signal.SIGBREAK) 
(note the negative pid value).

--

___
Python tracker 
<https://bugs.python.org/issue46716>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46726] Thread spuriously marked dead after interrupting a join call

2022-02-12 Thread Eryk Sun


Eryk Sun  added the comment:

> The race on := is much smaller than the original race 
> and I suspect in practice will be very hard to hit.

In Windows, the acquire() method of a lock can't be interrupted. Thus, in the 
main thread, an exception from Ctrl+C gets raised as soon as acquire() returns. 
This exception definitely will interrupt the assignment. Here's a workaround:

global scope:

_WINDOWS = _sys.platform == 'win32'

in _wait_for_tstate_lock():

acquired = None

try:
if acquired := lock.acquire(block, timeout):
lock.release()
self._stop()
except:
if _WINDOWS and acquired is None:
acquired = True
if acquired:
lock.release()
self._stop()
raise

This doesn't help in POSIX if the STORE_FAST instruction that assigns 
`acquired` gets interrupted. This can't be distinguished from acquire() itself 
getting interrupted. But at least the window for this is as small as possible.

--
nosy: +eryksun

___
Python tracker 
<https://bugs.python.org/issue46726>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46726] Thread spuriously marked dead after interrupting a join call

2022-02-12 Thread Eryk Sun


Eryk Sun  added the comment:

> If the acquire() in fact times out, but the store to the `acquired` 
> variable is interrupted, `if _WINDOWS and acquired is None` will
> succeed, despite that the lock is still locked

Yeah, my proposed workaround is no good, so we can't resolve this without a 
more fundamental solution. Are you looking into a way to prevent the STORE_FAST 
instruction from getting interrupted by an asynchronous exception?

--

___
Python tracker 
<https://bugs.python.org/issue46726>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46726] Thread spuriously marked dead after interrupting a join call

2022-02-12 Thread Eryk Sun


Eryk Sun  added the comment:

> Wrap everything needed in a custom C function. 

Maybe add an `acquire_and_release()` method:

static PyObject *
lock_PyThread_acquire_and_release_lock(
lockobject *self, PyObject *args, PyObject *kwds)
{
_PyTime_t timeout;

if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
return NULL;

PyLockStatus r = acquire_timed(self->lock_lock, timeout);

if (r == PY_LOCK_INTR) {
return NULL;
}

if (r == PY_LOCK_ACQUIRED) {
PyThread_release_lock(self->lock_lock);
self->locked = 0;
}

return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
}

--

___
Python tracker 
<https://bugs.python.org/issue46726>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46716] regrtest didn't respect the timeout when running test_subprocess on AMD64 Windows11 3.x

2022-02-13 Thread Eryk Sun


Eryk Sun  added the comment:

> the fix should be as simple as coercing the timeout values to >= 0.

Popen._remaining_time() should return max(endtime - _time(), 0).

Popen._wait() should raise OverflowError if the timeout is too large for the 
implementation. In Windows, the upper limit in milliseconds is 
`_winapi.INFINITE - 1` (about 49.7 days). It's important to only allow the 
timeout in milliseconds to be _winapi.INFINITE when `timeout is None`.

The DWORD converter in _winapi needs to subclass unsigned_long_converter. The 
current implementation based on the legacy format unit "k" is too lenient. 
Negative values and values that are too large should fail. I updated it to use 
the following definition:

class DWORD_converter(unsigned_long_converter):
type = 'DWORD'

This produces the following improved results:

>>> h = _winapi.GetCurrentProcess()
>>> _winapi.WaitForSingleObject(h, _winapi.INFINITE + 1)
Traceback (most recent call last):
  File "", line 1, in 
OverflowError: Python int too large to convert to C unsigned long

>>> _winapi.WaitForSingleObject(h, -1)
Traceback (most recent call last):
  File "", line 1, in 
ValueError: value must be positive

--

___
Python tracker 
<https://bugs.python.org/issue46716>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46726] Thread spuriously marked dead after interrupting a join call

2022-02-13 Thread Eryk Sun


Eryk Sun  added the comment:

> is there a bulletproof way to guarantee that `self._stop()` gets 
> called if the acquire_and_release() succeeds? 

I don't think it's critical. But we still should deal with the common case in 
Windows in which a KeyboardInterrupt is raised immediately after the method 
returns. For example:

try:
if lock.acquire_and_release(block, timeout):
self._stop
except:
if not lock.locked():
self._stop()

The proposed acquire_and_release() method can also be used in 
threading._shutdown(), when it iterates _shutdown_locks to join non-daemon 
threads.

--

___
Python tracker 
<https://bugs.python.org/issue46726>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46726] Thread spuriously marked dead after interrupting a join call

2022-02-13 Thread Eryk Sun


Eryk Sun  added the comment:

> Anything at the Python level that cares whether the thread is 
> still alive will call _wait_for_tstate_lock() again

It's nice that _maintain_shutdown_locks() gets called in _stop(), but the more 
important call site is in _set_tstate_lock().

I typed up the example initially with try/finally. Then I changed it to avoid 
the extra lock.locked() call when there's no exception, but I forgot to add a 
`raise` statement:

try:
if lock.acquire_and_release(block, timeout):
self._stop
except:
if not lock.locked():
self._stop()
raise

--

___
Python tracker 
<https://bugs.python.org/issue46726>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46726] Thread spuriously marked dead after interrupting a join call

2022-02-13 Thread Eryk Sun


Eryk Sun  added the comment:

> I didn't have that in mind at all.

I understood what you had in mind, and I don't disagree. I was just 
highlighting that the only somewhat important work done in _stop() is to clean 
up the _shutdown_locks set, to keep it from growing too large. But that task is 
already handled when creating a new thread, and it's arguably more important to 
handle it there.

--

___
Python tracker 
<https://bugs.python.org/issue46726>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46716] regrtest didn't respect the timeout when running test_subprocess on AMD64 Windows11 3.x

2022-02-14 Thread Eryk Sun


Eryk Sun  added the comment:

> I fear the potential 3rd-party breakage alone should bump
> this to its own issue.

The change to the DWORD converter in _winapi should only be in 3.11+. If this 
causes problems for other projects, they're probably depending on undefined 
behavior in the standard library. No third-party package or application should 
use _winapi directly.

> 1) modify Windows Popen._wait() to raise on out of bounds 
> values [< 0 or >= INFINITE]

I think raising ValueError would be best at this level. For example:

if timeout is None:
timeout_millis = _winapi.INFINITE
else:
timeout_millis = int(timeout * 1000)
if timeout_millis >= _winapi.INFINITE:
raise ValueError("'timeout' exceeds the platform limit")
if timeout_millis < 0:
raise ValueError("'timeout' must be non-negative")

--

___
Python tracker 
<https://bugs.python.org/issue46716>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue46716] regrtest didn't respect the timeout when running test_subprocess on AMD64 Windows11 3.x

2022-02-14 Thread Eryk Sun


Eryk Sun  added the comment:

> 4) use Job objects to group Windows processes for termination

I think a separate issue should be created for this enhancement.

_winapi wrappers would be needed for CreateJobObjectW(), 
SetInformationJobObject(), AssignProcessToJobObject(), TerminatejobObject(), 
and ResumeThread(), plus the constant CREATE_SUSPENDED. I'd also prefer to make 
related changes to send_signal(), which would require GetConsoleProcessList() 
and GenerateConsoleCtrlEvent().

A new Popen option would be needed to configure whether the job allows 
descendants to break away via the process creation flag 
CREATE_BREAKAWAY_FROM_JOB. This should be allowed by default.

---

send_signal(): SIGKILL, SIGTERM, SIBREAK, SIGINT

Support Popen.kill(group=False) and Popen.terminate(group=False) on all 
platforms as Popen.send_signal(signal.SIGKILL, group=group) and 
Popen.send_signal(signal.SIGTERM, group=group).

The Universal CRT (ucrt) in Windows doesn't define SIGKILL. Even when it's not 
defined by the platform, signal.SIGKILL should always be defined, preferably as 
9 (unused by ucrt), else as an unused value in the range up to NSIG, else as 
NSIG + 1.

The `group` keyword-only option broadens the scope to the process group or job. 
A process is a group leader if it was created with the flag 
CREATE_NEW_PROCESS_GROUP (save self._creationflags) and its process ID is in 
GetConsoleProcessList().

For SIGKILL, always use forced termination. For SIGTERM, use forced termination 
either if `group` is false or if `group` is true and the process is not a group 
leader. To force termination, call TerminateJobObject(self._job_handle, 1) if 
`group` is true, else TerminateProcess(self._handle, 1). 

For SIGTERM, SIGBREAK, and SIGINT, call GenerateConsoleCtrlEvent() if `group` 
is true and the process is a group leader. For SIGTERM and SIGBREAK, send 
CTRL_BREAK_EVENT. For SIGINT, send CTRL_C_EVENT. 

Behavior to deprecate:

When `group` is false and `sig` is CTRL_C_EVENT (0) or CTRL_BREAK_EVENT (1), 
send_signal() always calls os.kill(). This legacy behavior tries to handle a 
process as if it's a process group, and it combines POSIX kill() with Windows 
API constants. subprocess should distance itself from the fundamental problems 
with os.kill() in Windows.

--

___
Python tracker 
<https://bugs.python.org/issue46716>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



  1   2   3   4   5   6   7   8   9   10   >